TO: Akshay Malik, City of Philadelphia Smart Cities Director FROM: Kamya Khandelwal, Revathi Machan, Claudia Schreier DATE: 13 May 2024 RE: Predictive modeling for gentrification in Philadelphia

Gentrification in Philadelphia

Philadelphia has been experiencing a cost-of-living crisis for decades. For a city commonly referred to as the nation’s poorest largest city, there is much worry about the future of new developments and housing1. Philadelphia has the highest poverty rate out of the ten largest cities in the US. In 2018, around 231,000 Philadelphia households were cost-burdened, which is defined by the US Department of Housing and Urban Development as a situation when a household spends 30% or more of its income on housing costs, including rent, mortgage payments, utilities, insurance, and property taxes2. This problem is more severe for renters – among renters with incomes below $30,000 per year, 88% are cost-burdened and 68% are severely cost-burdened (spending more than 50% of income on housing).

With lots of cost-burdened householders, including homeowners and renters, in the city, it seems that the fear of displacement and gentrification in many neighborhoods is common. Historically high rents and mortgage rates, combined with unprecedented levels of new developments have spurred discussion around gentrification in the city4. Gentrification is defined as a type of neighborhood change where higher income residents replace lower income ones – housing costs rise, property values rise, and typically, long term residents get displaced5. Gentrification today is informed by a legacy of displacement that was spurred by ”redevelopment” and ”urban renewal” plans. Philadelphia was hit especially hard by this displacement in West Philadelphia with university expansion plans throughout the second half of the 20th century.

In broader discussions about gentrification online and in media, gentrification can seem like something that occurs when a brewery replaces a corner store, or a luxury gym replaces a single-family home. Gentrification can be marked by many things, and the signs can vary for different people that a neighborhood is being gentrified. There has even been new technology like AI models created to identify signs of gentrification from temporal maps like Google Street View6. While the signs of gentrification can be visible, they may be hard to track through data. Researchers rely on demographic data to make conclusions about the types of change that the neighborhood is experiencing, like racial changes, age changes, and income changes.

Data exploration

This project seeks to create a predictive model for gentrification that can be used in planning practice to identify areas at risk for gentrification or identify areas undergoing gentrification. This model uses factors that the team at Marie Antoinette Predictions have researched and believe are reliable markers for gentrification. The variables in our model are construction permits, vacant properties, affordable housing locations, green spaces, and demolitions. The data was collected from Philadelphia’s open data portal7. After downloading the relevant data, it was cleaned to ensure that variables were clear and transferable.

mapTheme <- function(base_size = 12) {
  theme(
    text = element_text( color = "black"),
    plot.title = element_text(size = 16,colour = "black"),
    plot.caption=element_text(hjust=0),
    axis.ticks = element_blank(),
    panel.background = element_blank(),
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_rect(colour = "black", fill=NA, size=2),
    strip.text.x = element_text(size = 14))
}

plotTheme <- function(base_size = 12) {
  theme(
    text = element_text( color = "black"),
    plot.title = element_text(color = "darkgreen", size=15, face="bold"),
    plot.subtitle = element_text(face="italic"),
    plot.caption = element_text(hjust=0),
    axis.ticks = element_blank(),
    panel.background = element_blank(),
    panel.grid.major = element_line("grey80", size = 0.1),
    panel.grid.minor = element_blank(),
    panel.border = element_rect(colour = "black", fill=NA, size=2),
    strip.background = element_rect(fill = "grey80", color = "white"),
    strip.text = element_text(size=12),
    axis.title = element_text(size=12),
    axis.text = element_text(size=10),
    plot.background = element_blank(),
    legend.background = element_blank(),
    legend.title = element_text(colour = "black", face = "italic"),
    legend.text = element_text(colour = "black", face = "italic"),
    strip.text.x = element_text(size = 14)
  )
}

Read in Data from Philadelphia

# permits
phlPermits <- 
  st_read("https://phl.carto.com/api/v2/sql?q=SELECT+*+FROM+permits+WHERE+permitissuedate+>=+'2013-01-01'+AND+permitissuedate+<+'2022-12-31'&filename=permits&format=geojson&skipfields=cartodb_id") %>% 
    st_transform('ESRI:102728')%>%
  mutate(Legend = "Philadelphia Permits")%>%
  mutate(Year = as.integer(format(permitissuedate, "%Y"))) 

## 2. Philadelphia Boundaries
phlBoundary <- 
  st_read("http://data.phl.opendata.arcgis.com/datasets/405ec3da942d4e20869d4e1449a2be48_0.geojson") %>%
  st_transform('ESRI:102728')

#dunno if we need to fishnet this
phlFishnet <- 
  st_make_grid(phlBoundary,
               cellsize = 500, 
               square = TRUE) %>%
  .[phlBoundary] %>% 
  st_sf() %>%
  mutate(uniqueID = rownames(.))

## 3. Philadelphia Neighborhoods - isn't loading
phillyNeighborhoods <-
  st_read("https://raw.githubusercontent.com/opendataphilly/open-geo-data/master/philadelphia-neighborhoods/philadelphia-neighborhoods.geojson") %>%
  st_transform('ESRI:102728') %>%
  st_transform(st_crs(phlFishnet)) 

## 4. Vacant Property
vacantBuilding <-
  st_read('https://opendata.arcgis.com/datasets/f7ed68293c5e40d58f1de9c8435c3e84_0.geojson') %>% 
  na.omit() %>%
  st_transform('ESRI:102728') %>%
  st_transform(st_crs(phlFishnet)) %>%
  mutate(Legend = "Vacant Buildings")

## 5. Affordable Housing
affordableHousing <-
  st_read('https://opendata.arcgis.com/datasets/ca8944190b604b2aae7eff17c8dd9ef5_0.geojson') %>% 
  filter(FISCAL_YEAR_COMPLETE >= "2012") %>%
  st_transform('ESRI:102728') %>%
  st_transform(st_crs(phlFishnet)) %>%
  mutate(Legend = "Affordable Housing")


## 7. Green Spaces
greenSpace <-
  st_read('https://opendata.arcgis.com/datasets/d52445160ab14380a673e5849203eb64_0.geojson') %>% 
  st_transform('ESRI:102728') %>%
  st_transform(st_crs(phlFishnet)) %>%
  mutate(Legend = "Green Spaces")


## 10. Demolition Data
buildingDemolition <-
  st_read('https://phl.carto.com/api/v2/sql?q=SELECT+*+FROM+demolitions&filename=demolitions&format=geojson&skipfields=cartodb_id') %>% 
  mutate(year = substr(start_date,1,4)) %>%
  filter(year == '2022') %>%
  st_transform('ESRI:102728') %>%
  st_transform(st_crs(phlFishnet)) %>%
  mutate(Legend = "Building Demolition") 

## 11. Census data - ACS 2021
tracts22 <- get_acs(
  geography = "tract",
  variables = c(
    "B01003_001",   # Total Population
    "B19013_001",   # Median Household Income
    "B25008_002",   # Owner-Occupied Units
    "B25008_003",   # Renter-Occupied Units
    "B25008_001E",  # Total Population in Housing Units
    "B15003_022",   # Educational Attainment: Bachelor's Degree
    "B06012_002E",  # Population Below the Poverty Level
    "B02001_002",   # Race and Ethnicity: White Alone
    "B02001_003",   # Race and Ethnicity: Black or African American Alone
    "B27011_008E"  # Population Unemployed
  ),
  year = 2022,
  state = "PA",
  county = "Philadelphia",
  geometry = TRUE,
  output = "wide"
)%>%
  dplyr::select(-NAME, -ends_with("M")) %>%
  rename(totalPop = B01003_001E,                           # Total Population
         medHHInc = B19013_001E,                           # Median Household Income
         totalUnit = B25008_001E,                          # Total Population in Housing Units
         ownerOccupied = B25008_002E,                      # Owner-Occupied Units
         renterOccupied = B25008_003E,                     # Renter-Occupied Units
         bachDegree = B15003_022E,                    # Educational Attainment: Bachelor's Degree
         totalPov = B06012_002E,                       # Population Below the Poverty Level
         totalUnemploy = B27011_008E,                  # Population Unemployed
         whiteAlone = B02001_002E,                          # Race and Ethnicity: White Alone
         blackAlone = B02001_003E                          # Race and Ethnicity: Black or African American Alone
         )

### 11.1. Transform the data to ESRI:102728 projection

tracts22 <- tracts22 %>% st_transform(st_crs(phlFishnet))

### 11.2 Create new variables

tracts22 <- tracts22 %>%
  mutate(pctWhite = ifelse(totalPop > 0, whiteAlone / totalPop * 100,0),
         pctBlack = ifelse(totalPop > 0, blackAlone / totalPop * 100,0),
         pctPov = ifelse(totalPop > 0, totalPov / totalPop *100, 0),
         pctUnemploy = ifelse(totalPop > 0, totalUnemploy / totalPop *100, 0),
         pctBach = ifelse(totalPop > 0, bachDegree / totalPop *100, 0),
         pctOwnerOccupied = ifelse(totalPop > 0, ownerOccupied / totalUnit *100, 0),
         pctRenterOccupied = ifelse(totalPop > 0, renterOccupied / totalUnit *100, 0)
         ) %>%
  dplyr::select(-whiteAlone, -blackAlone, -totalPov ,-totalUnemploy,-ownerOccupied, -renterOccupied, -totalUnit, -bachDegree, -GEOID) %>%
  st_transform(st_crs(phlFishnet)) 

### 11.3 Organize into datasets - what is this whole organization part for??

tracts22.medHHInc <- tracts22 %>%
  dplyr::select(medHHInc) %>%
  rename(Legend = medHHInc)

tracts22.pctWhite <- tracts22 %>%
  dplyr::select(pctWhite)%>%
  rename(Legend = pctWhite)

tracts22.pctBlack <- tracts22 %>%
  dplyr::select(pctBlack)%>%
  rename(Legend = pctBlack)

tracts22.pctPov <- tracts22 %>%
  dplyr::select(pctPov)%>%
  rename(Legend = pctPov)

tracts22.pctUnemploy <- tracts22 %>%
  dplyr::select(pctUnemploy)%>%
  rename(Legend = pctUnemploy)

tracts22.pctBach <- tracts22 %>%
   dplyr::select(pctBach)%>%
   rename(Legend = pctBach)

tracts22.pctOwnerOccupied <- tracts22 %>%
  dplyr::select(pctOwnerOccupied)%>%
  rename(Legend = pctOwnerOccupied)

tracts22.pctRenterOccupied <- tracts22 %>%
  dplyr::select(pctRenterOccupied)%>%
  rename(Legend = pctRenterOccupied)

Data Visualisation

The data was visualized using mapping tools. The area from around Center City to lower North Philadelphia and Kensington has the densest amount of construction permits and demolition permits. There is a high density of vacant buildings in West and North Philadelphia.

#Categorizing the permits for construction and demolition
phlPermits <- phlPermits %>%
  mutate(newType = case_when(permittype == "BUILDING" | permittype == "BP_NEWCNST"  ~ 'CONSTRUCTION PERMIT',
  permittype == "DEMOLITION" | permittype == "BP_DEMO" ~ 'DEMOLITION PERMIT'))


cnstPermits <- phlPermits %>%
  filter(newType == 'CONSTRUCTION PERMIT')

demoPermits <- phlPermits %>%
  filter(newType == 'DEMOLITION PERMIT')

cnstPermits_2022 <- cnstPermits%>%
  filter(Year==2022)

cnstPermits_2013 <- cnstPermits%>%
  filter(Year == 2013)

The plots below show how the different types of permits are located all over Philadelphia, looking primarily at the the permits that fall under construction or zoning. We also look at how other various chosen demographic and socio-economic factors are mapped out across the city to compare them against where there is the highest density of permits. Construction permits can be seen in higher concentrations in the areas where there is a higher percentage of individuals with bachelor’s degrees and higher unemployment.

# Plot 1: map of all construction and demo permits issued b/w 2013 and 2022
ggplot() + 
    geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
    geom_sf(data = phlPermits, aes(colour = newType), size = 0.5, show.legend = "point") +
  scale_color_manual(values = c("magenta", "lightgreen")) +
  labs(title = "Major Permits Issued, 2013-22 in Philadelphia",
       caption = "Figure 1") +
    theme_void()  

  # Plot 2 + 3: Mapped points and Density map of construction permits issued
ggplot() + 
  geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
  geom_sf(data = cnstPermits, aes(color = "magenta"), size = 0.5) + 
  scale_color_identity(guide = "legend", name = NULL, labels = "Construction Permits") +
  labs(title = "Construction Permits Issued, 2013-22 in Philadelphia",
       caption = "Figure 2") +
  theme_void() 

  ggplot() + 
    geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
    stat_density2d(data = data.frame(st_coordinates(cnstPermits)),  
                   aes(X, Y, fill = ..level.., alpha = ..level..),  
                   size = 0.01, bins = 40, geom = 'polygon') +  
    scale_fill_viridis_c(option ="rocket") +  
    labs(title = "Density of Construction Permits") +  
    theme_void() + theme(legend.position = "none")  

#Plot 4 + 5: Mapped points and Density map of demolition permits issued
ggplot() + 
    geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
    geom_sf(data = demoPermits, aes(colour = "lightgreen"), size = 0.5) +
  scale_color_identity(guide = "legend", name = NULL, labels = "Demolition Permits") +
  labs(title = "Demolition Permits Issued, 2013-22 in Philadelphia",
       caption = "Figure 4") +
    theme_void()

ggplot() + 
    geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
    stat_density2d(data = data.frame(st_coordinates(demoPermits)),  
                   aes(X, Y, fill = ..level.., alpha = ..level..),  
                   size = 0.01, bins = 40, geom = 'polygon') +  
    scale_fill_viridis_c(option = "rocket") +  
    scale_alpha(range = c(0.00, 0.35), guide = FALSE) +  
    labs(title = "Density of Construction Permits") +  
    theme_void() + theme(legend.position = "none")  

# plot 6: affordable housing
ggplot() + 
  geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
  geom_sf(data = affordableHousing, aes(color = "orange"), size = 0.5) + 
  scale_color_identity(guide = "legend", name = NULL, labels = "Affordable Housing Units") +
  labs(title = "Affordable Housing Developments, 2013-22 in Philadelphia",
       caption = "Figure 6") +
  theme_void() 

#plot7: 
ggplot() + 
  geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
  geom_sf(data = greenSpace, aes(color = "darkgreen"), size = 0.5) + 
  scale_color_identity(guide = "legend", name = NULL, labels = "Green Spaces") +
  labs(title = "Green Spaces, 2013-22 in Philadelphia",
       caption = "Figure 7") +
  theme_void() 

#plot8 and 9: vacant land point and density maps
ggplot() + 
    geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
    geom_sf(data = vacantBuilding, aes(color = 'darkred'), size = 0.5, show.legend = "point") +
  scale_color_identity(guide = "legend", name = NULL, labels = "Vacant Buildings") +
  labs(title = "Suspected Vacant Buildings in Philadelphia",
       caption = "Figure 8") +
    theme_void()

ggplot() + 
    geom_sf(data = phlBoundary, fill = "grey89", color = "darkgrey") +  
    stat_density2d(data = data.frame(st_coordinates(vacantBuilding)),  
                   aes(X, Y, fill = ..level.., alpha = ..level..),  
                   size = 0.01, bins = 40, geom = 'polygon') +  
    scale_fill_viridis_c(option = "rocket") +  
    scale_alpha(range = c(0.00, 0.35), guide = FALSE) + 
    labs(title = "Density of Vacant Buldings") +  
    theme_void() + theme(legend.position = "none")  

#plot10: demographic
ggplot()+
    geom_sf(data=tracts22, aes(color=NA, fill=pctUnemploy))+
    scale_color_viridis()+
    scale_fill_viridis_c()+
    geom_sf(data = cnstPermits, aes(color="yellow") ,
          show.legend = "point", size = .1, alpha=0.3) +
    scale_color_identity() +
    labs(title="% Unemployment around Permits Issued")+
    mapTheme()+theme(plot.title = element_text(size = 10), legend.title=element_blank())

ggplot()+
    geom_sf(data=tracts22, aes(color=NA, fill=pctBach))+
    scale_color_viridis()+
    scale_fill_viridis()+
    geom_sf(data = cnstPermits, aes(color="yellow") ,
          show.legend = "point", size = .1, alpha=0.3) +
    scale_color_identity() +
    labs(title="% Population with Bachelor's Degree around Permits Issued")

### Fishnet

Here we construct a geospatial dataset to examine how the risk of new development is spread throughout Philadelphia. To do this we created a fishnet which is a continous grid pattern consisting of cells measuring 500x500 feet. This grid allows us to conceptualize development risk, represented by the presence of new construction permits. The following plot depicts the count of new construction permits across the fishnet, with grid cells depicted in yellow indicating the highest permit counts.

# Attaching datasets on spatial factors to Fishnet

## 1. Extracting geometry for spatial factors


affordableHousings <- affordableHousing %>%
  dplyr::select(geometry, Legend)

vacantBuildings <- vacantBuilding %>%
  dplyr::select(geometry, Legend)

greenSpaces <- greenSpace %>%
  dplyr::select(geometry, Legend)

buildingDemolitions <- buildingDemolition %>%
  dplyr::select(geometry, Legend)


## 2. Creating fishnet of spatial factor variables 

vars_net <- 
  rbind(affordableHousings, vacantBuildings,
        greenSpaces, buildingDemolitions) %>%
  st_join(., phlFishnet, join=st_within) %>%
  st_drop_geometry() %>%
  group_by(uniqueID, Legend) %>%
  summarize(count = n()) %>%
  full_join(phlFishnet, by = "uniqueID") %>%
  spread(Legend, count, fill=0) %>%
  st_sf() %>%
  na.omit() %>% 
  dplyr::select(-`<NA>`) %>%
  ungroup()


cnstPermits <- st_transform(cnstPermits, st_crs(phlFishnet))

construction_net <- 
  dplyr::select(cnstPermits) %>% 
  mutate(countPermits = 1) %>% 
  aggregate(., phlFishnet, sum) %>%
  mutate(countPermits = replace_na(countPermits, 0),
         uniqueID = rownames(.),
         cvID = sample(round(nrow(phlFishnet) / 24), 
                       size=nrow(phlFishnet), replace = TRUE))

# visualising permits joined to fishnet

ggplot() +
  geom_sf(data = construction_net, aes(fill = countPermits), color = NA) +
  geom_sf(data = st_boundary(phlFishnet), color = "darkred", lwd = .04, alpha=.2) + # Add boundaries in red
  scale_fill_viridis_c(option = "plasma",
                       name = 'Construction Counts') +
  labs(title = "Construction Permits Joined to Fishnet",
       subtitle = 'Philadelphia') + mapTheme() + plotTheme()

### Nearest Neighbour

This code segment creates a nearest neighbor feature for different categories, including vacant buildings, affordable housing, green spaces, and building demolitions. For each category, it first maps the nearest feature from a given dataset to another dataset, converts the datasets to rsgeo geometries, calculates the Euclidean distance between each observation in one dataset and its nearest neighbor in the other dataset. Finally, the calculated distances are appended as new variables to the original dataset for each category, such as dist_vacantBuilding, dist_affordableHousing, dist_greenSpace, and dist_buildingDemolition. Essentially this process turns each individual grid cell into a centroid point and then measures the distances between each to find the nearest risk factor point.

This spatial analysis is crucial for understanding the relationships between different urban features and their proximity to one another. By calculating the distances between variables like vacant buildings, affordable housing, green spaces, and building demolitions, it provides insights into the spatial distribution and potential interactions among these features.

## 1.2. Vacant Buildings

### Mapping nearest feature

nearest_vacantBuilding <- sf::st_nearest_feature(vars_net, vacantBuilding)

### Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(vars_net)
y <- rsgeo::as_rsgeo(vacantBuilding)

### Calculating distance

vars_net$dist_vacantBuilding <- rsgeo::distance_euclidean_pairwise(x, y[nearest_vacantBuilding])


## 1.3. Affordable Housing

### Mapping nearest feature

nearest_affordableHousing <- sf::st_nearest_feature(vars_net, affordableHousing)

### Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(vars_net)
y <- rsgeo::as_rsgeo(affordableHousing)

### Calculating distance

vars_net$dist_affordableHousing <- rsgeo::distance_euclidean_pairwise(x, y[nearest_affordableHousing])


## 1.4. Green Spaces

### Mapping nearest feature

nearest_greenSpace <- sf::st_nearest_feature(vars_net, greenSpace)

### Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(vars_net)
y <- rsgeo::as_rsgeo(greenSpace)

### Calculating distance

vars_net$dist_greenSpace <- rsgeo::distance_euclidean_pairwise(x, y[nearest_greenSpace])

## 1.8. Building Demolitions

### Mapping nearest feature

nearest_buildingDemolition <- sf::st_nearest_feature(vars_net, buildingDemolition)

### Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(vars_net)
y <- rsgeo::as_rsgeo(buildingDemolition)

### Calculating distance

vars_net$dist_buildingDemolition <- rsgeo::distance_euclidean_pairwise(x, y[nearest_buildingDemolition])
# 2. Visualizing nearest distance for spatial factors on Fishnet

## 2.1. Visualizing the nearest three features

vars_net.long.nn <- 
  dplyr::select(vars_net, starts_with("dist")) %>%
  gather(Variable, value, -geometry)

vars <- unique(vars_net.long.nn$Variable)
mapList <- list()

for(i in vars){
  mapList[[i]] <- 
    ggplot() +
    geom_sf(data = filter(vars_net.long.nn, Variable == i), aes(fill=value), colour=NA) +
    scale_fill_viridis_c(option = "plasma",
                         name = " ") +
    labs(title=i) +
    mapTheme()+
    theme(plot.title = element_text(size = 12, color = "black"))
  }

bottomCaption <- textGrob("Figure 8", gp = gpar(hjust = 0))

do.call(grid.arrange, c(list(grobs = mapList, ncol = 2, 
                             top = textGrob("Spatial Factors: Nearest Neighbor Distance for Permits Issued\n", 
                                            gp = gpar(fontsize = 15, fontface = "bold", col = "darkred")), 
                             bottom = bottomCaption)))

Spatial Joins

Here we want to join the census data to our fishnet so we can also map our demographic variables in a similar fashion, and also just standardize our mode of analysis across all our data.

# Joining Census Data to Fishnet

tracts22 <- tracts22 %>%
  filter(totalPop>0)

vars_net <-
  vars_net%>%
  st_centroid()%>%
  st_join(tracts22)


vars_net <- vars_net %>% mutate_all(~replace(., is.na(.), 0))

# Perform Spatial Join of variables with permits

final_net <-
  left_join(construction_net, st_drop_geometry(vars_net), by="uniqueID") # this one doesn't work so the last one won't either

# Final Net


final_net <-
  st_centroid(final_net) %>% 
    st_join(dplyr::select(phillyNeighborhoods, name), by = "uniqueID") %>% 
      st_drop_geometry() %>%
      left_join(dplyr::select(final_net, geometry, uniqueID)) %>%  
      st_sf() %>%
  na.omit()
## generates warnings from PROJ issues
## {spdep} to make polygon to neighborhoods... 
final_net.nb <- poly2nb(as_Spatial(final_net), queen=TRUE)
## ... and neighborhoods to list of weigths
final_net.weights <- nb2listw(final_net.nb, style="W", zero.policy=TRUE)

# print(final_net.weights, zero.policy=TRUE)

Modeling Process

Local Moran’s I

Incorporating Local Moran’s I analysis into the spatial modeling process is pivotal for understanding the distribution of construction permits across urban areas. Local Moran’s I serves as a vital tool, providing insights into the spatial clustering or dispersion of permit issuance, thereby enhancing the model’s capacity to identify significant spatial patterns in urban development. By integrating Local Moran’s I statistics with other spatial factors, such as nearest neighbor distances and geographic features, the model gains a comprehensive understanding of the underlying spatial dynamics influencing construction permit distributions, thereby facilitating informed urban planning and policy decisions.In the context of this model, Local Moran’s I helps identify significant spatial patterns in construction permit issuance, indicating areas with high or low concentrations of permits.

Initially, the local Moran’s I values are computed for the permit counts using spatial weights. Then, these results are joined with the spatial data, and significant hotspots are identified based on a significance threshold of 0.05. The output is organized into a long format for plotting, where each variable’s Local Moran’s I statistics are visualized as spatial maps. These maps depict the spatial clustering patterns of permit issuance, indicating areas of significant clustering or dispersion.

## see ?localmoran
local_morans <- localmoran(final_net$countPermits, final_net.weights, zero.policy=TRUE) %>% 
  as.data.frame()

# join local Moran's I results to fishnet
final_net.localMorans <- 
  cbind(local_morans, as.data.frame(final_net)) %>% 
  st_sf() %>%
  dplyr::select(Permit_Count = countPermits, 
                Local_Morans_I = Ii, 
                P_Value = `Pr(z != E(Ii))`) %>%
  mutate(Significant_Hotspots = ifelse((P_Value <= 0.05), 1, 0)) %>%
  gather(Variable, Value, -geometry)
## This is just for plotting
vars <- unique(final_net.localMorans$Variable)
varList <- list()

for(i in vars){
  varList[[i]] <- 
    ggplot() +
      geom_sf(data = filter(final_net.localMorans, Variable == i), 
              aes(fill = Value), colour=NA) +
      scale_fill_viridis(name="") +
      labs(title=i) +
      theme_void() + theme(legend.position="bottom")}

do.call(grid.arrange,c(varList, ncol = 4, top = "Local Morans I Statistics, Construction Permits"))

saveRDS(local_morans, file = "local_morans.rds")

Distance to Hotspots

Shorter distances between permits indicate areas with higher construction density, which can be associated with rapid urban development or gentrification.

local_morans <- readRDS("local_morans.rds")

final_net <- final_net %>%
  mutate(permit.isSig = ifelse(local_morans[,5] <= 0.0000001,1,0)) %>%
  mutate(permit.isSig.dist = nn_function(st_coordinates(st_centroid(final_net)), st_coordinates(st_centroid(filter(final_net, permit.isSig == 1))), 1))

#plotting NN hotspots

ggplot() +
  geom_sf(data = final_net, aes(fill=permit.isSig.dist), colour = NA) +
  scale_fill_viridis(name="NN Distance") +
      labs(title="Permit NN Distance", caption= "Figure xx") +
      mapTheme()+
      plotTheme()

#lmoran <- localmoran(final_net$lightsout, final_net.weights,  zero.policy=TRUE)

#final_net$lmI <- lmoran[, "Ii"] # local Moran's I
#final_net$lmZ <- lmoran[, "Z.Ii"] # z-scores
#final_net$lmp <- lmoran[, "Pr(z != E(Ii))"]


#mp <- moran.plot(as.vector(scale(final_net$lightsout)), final_net.weights, zero.policy = TRUE)

##Create a hotspot variable:
#final_net$lmp <- ifelse(is.nan(final_net$lmp), 0.10, final_net$lmp)
#final_net$hotspot <- 0
#final_net[(mp$x >=0 & mp$wx >=0) & final_net$lmp <= 0.05, "hotspot"]<- 1
# generates warning from NN
#final_net <- final_net %>% 
  #mutate(lightsout.isSig.dist = 
           #nn_function(st_c(st_coid(final_net)),
                       #st_c(st_coid(filter(final_net, 
                                      #     hotspot == 1))), 
#                       k = 1))
#ggplot() +
#      geom_sf(data = final_net, aes(fill=lightsout.isSig.dist), colour=NA) +
#      scale_fill_viridis(name="NN Distance") +
#      labs(title="Alley Lights Out NN Distance") +
#      theme_void()

Building and evaluating the model

The model used in this analysis is a poisson regression model. The collected and cleaned data that was used in this model was demographic information like race, income, and work status; housing information like percent renter and owner occupied; regional characteristics like vacant buildings, construction permits, affordable housing, and demolitions.

Correlation

correlation.long <-
  st_drop_geometry(final_net) %>%
    dplyr::select(-uniqueID, -cvID, -name) %>%
    gather(Variable, Value, -countPermits) %>%
  mutate(Value = as.numeric(Value))

correlation.cor <-
  correlation.long %>%
    group_by(Variable) %>%
    summarize(correlation = cor(Value, countPermits, use = "complete.obs"))

# Visualizing correlations through scatter plots
#we have a lot of demographic variables here that i don't know if we necessarily need or are interested in keeping for our final stuff - think it may be better to pick and choose fewer demo variables and maybe choose more external variables
    
ggplot(correlation.long, aes(Value, countPermits)) +
  geom_point(size = 0.1) +
  geom_text(data = correlation.cor, aes(label = paste("r =", round(correlation, 2))),
            x=-Inf, y=Inf, vjust = 1.5, hjust = -.1) +
  geom_smooth(method = "lm", se = FALSE, colour = "orange") +
  facet_wrap(~Variable, ncol = 4, scales = "free") +
  labs(title = "Permit Count as a function of risk factors", caption="Figure 12") +
  plotTheme()

numvars <- c("countPermits", "dist_vacantBuilding",  "dist_affordableHousing","dist_greenSpace", "dist_buildingDemolition", "totalPop", "medHHInc", "pctWhite", "pctBlack", "pctBach" ,"pctPov", "pctUnemploy", "pctOwnerOccupied", "pctRenterOccupied")

numeric <- final_net %>%
  st_drop_geometry(final_net) %>%
  dplyr::select(numvars)%>%
  na.omit()

ggcorrplot(
  round(cor(numeric), 1), 
  p.mat = cor_pmat(numeric),
  colors = c('#d7191c','white','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across Variables\n", caption="Figure 13")+ 
    theme(plot.title = element_text(size = 11, face = "bold", color = "darkred"))+
    theme(axis.text.x=element_text(size=8))+
    theme(axis.text.y=element_text(size=8))

There seems to be relatively high positive correlation between median household income and the percent of the population that is white. This may suggest that areas with more white residents are higher earning neighborhoods, or a neighborhood that is gentrifying may have more higher income and more white residents. There is a relatively high negative correlation between total population and the amount of vacant buildings. This may mean that there are less vacant buildings in more populous areas - this could relate to gentrification in the way that more crowded, coveted neighborhoods will be more pressed for space, leading to redevelopment of vacant properties.

Conducting a regression model in this context is crucial for several reasons. Firstly, it allows for the quantification of relationships between various factors and the outcome of interest, such as the number of permits issued or the likelihood of development. This enables the identification of significant predictors and their respective impact on the outcome, aiding in understanding the underlying dynamics of development risk. Additionally, regression modeling provides a means to assess the relative importance of different variables, prioritize interventions, and inform decision-making processes aimed at mitigating development-related challenges or promoting sustainable urban growth.

Cross-validation is performed to evaluate the predictive performance of Poisson regression models applied to the dataset. This process involves splitting the dataset into distinct folds, where each fold serves as a training set for model development and a testing set for performance assessment. Within each iteration, a Poisson regression model is trained using the training data and then applied to the testing data to make predictions. These predictions are compared against the actual values to assess the model’s accuracy and robustness. The cross-validation procedure helps ensure that the models generalize well to unseen data and provide reliable predictions in real-world scenarios, enhancing the overall reliability of the analysis.

The provided code implements a cross-validation procedure for evaluating Poisson regression models applied to the dataset final_net. It defines a function crossValidate() to conduct cross-validation, where the dataset is split into distinct folds (cvID) for training and testing. Within each fold, a Poisson regression model is trained using the training data (fold.train) and then applied to the testing data (fold.test) to make predictions. These predictions are aggregated across all folds to assess the overall predictive performance of the model. The cross-validation helps validate the model’s ability to generalize to unseen data, ensuring the reliability of the regression analysis.

Variable Selection

Based on the correlation analysis, variables such as ‘Vacant Building Distance’, ‘Affordable Housing Distance’, ‘Green Space Distance’, ‘Building Demolition Distance’, ‘Median Household Income’, ‘Percent Below Poverty Line’, ‘Percent Unemployed’, and ‘Percent Owner Occupied’ are selected as model variables.

## define the variables we want
reg.vars <- c("dist_vacantBuilding",  "dist_affordableHousing","dist_greenSpace", "dist_buildingDemolition", "medHHInc", "pctBach" ,"pctPov", "pctUnemploy", "pctOwnerOccupied")

reg.ss.vars <- c("dist_vacantBuilding",  "dist_affordableHousing","dist_greenSpace", "dist_buildingDemolition", "medHHInc", "pctBach" ,"pctPov", "pctUnemploy", "pctOwnerOccupied", "permit.isSig")

## creating functions for cross validation

crossValidate <- function(dataset, id, dependentVariable, indVariables) {
  
  allPredictions <- data.frame()
  cvID_list <- unique(dataset[[id]])
  
  for (i in cvID_list) {
    
    thisFold <- i
    cat("This hold out fold is", thisFold, "\n")
    
    fold.train <- filter(dataset, dataset[[id]] != thisFold) %>% as.data.frame() %>% 
      dplyr::select(id, geometry, indVariables, dependentVariable)
    fold.test  <- filter(dataset, dataset[[id]] == thisFold) %>% as.data.frame() %>% 
      dplyr::select(id, geometry, indVariables, dependentVariable)
    
    regression <-
      glm(countPermits ~ ., family = "poisson", 
          data = fold.train %>% 
            dplyr::select(-geometry, -id))
    
    thisPrediction <- 
      mutate(fold.test, Prediction = predict(regression, fold.test, type = "response"))
    
    allPredictions <-
      rbind(allPredictions, thisPrediction)
    
  }
  return(st_sf(allPredictions))
}

Cross Validation

This shows the process of conducting cross-validation on Poisson regression models using the crossValidate function, aimed at predicting the number of construction permits (countPermits) in Philadelphia neighborhoods. Different models are being evaluated: a non-spatial process model using a defined set of variables (reg.vars) and a spatial process model, which integrates spatial characteristics along with the same set of variables to understand how including spatial data influences the accuracy and predictiveness of the model outcomes. This cross-validation approach helps in assessing the model’s robustness and generalizability by testing it on multiple subsets of data.

#conducting cross validation on Poisson regressino models

reg.cv <- crossValidate(
  dataset = final_net,
  id = "cvID",
  dependentVariable = "countPermits",
  indVariables = reg.vars) %>%
    dplyr::select(cvID = cvID, countPermits, Prediction, geometry)

reg.ss.cv <- crossValidate(
  dataset = final_net,
  id = "cvID",
  dependentVariable = "countPermits",
  indVariables = reg.ss.vars) %>%
    dplyr::select(cvID = cvID, countPermits, Prediction, geometry)

final_net$name <- ifelse(is.na(final_net$name), "UNKNOWN", final_net$name)
  
reg.spatialCV <- crossValidate(
  dataset = final_net,
  id = "name",
  dependentVariable = "countPermits",
  indVariables = reg.vars) %>%
    dplyr::select(cvID = name, countPermits, Prediction, geometry)

reg.ss.spatialCV <- crossValidate(
  dataset = final_net,
  id = "name",
  dependentVariable = "countPermits",
  indVariables = reg.ss.vars) %>%
    dplyr::select(cvID = name, countPermits, Prediction, geometry)

Error Calculation

In each regression, the absolute error is determined by calculating the difference between the predicted and the actual observed counts of new construction permits.

# calculate errors

reg.summary <- 
  rbind(
    mutate(reg.cv,           Error = Prediction - countPermits,
                             Regression = "Random k-fold CV: Just Risk Factors"),
                             
    mutate(reg.ss.cv,        Error = Prediction - countPermits,
                             Regression = "Random k-fold CV: Spatial Process"),
    
    mutate(reg.spatialCV,    Error = Prediction - countPermits,
                             Regression = "Spatial LOGO-CV: Just Risk Factors"),
                             
    mutate(reg.ss.spatialCV, Error = Prediction - countPermits,
                             Regression = "Spatial LOGO-CV: Spatial Process")) %>%
    st_sf()
# Calculate MAE and standard deviation for each fold and method
error_by_reg_and_fold <- 
  reg.summary %>% 
  group_by(Regression, cvID) %>% 
  summarize(Mean_Error = mean(Prediction - countPermits, na.rm = T),
    MAE = mean(abs(Mean_Error), na.rm = TRUE),
    SD_MAE = mean(abs(Mean_Error), na.rm = TRUE),
    .groups = 'drop'
  )

# Arrange by MAE for viewing
error_by_reg_and_fold %>% 
  arrange(desc(MAE))
error_by_reg_and_fold %>% 
  arrange(MAE)

# Plot histogram of OOF errors for each method
error_by_reg_and_fold %>%
  ggplot(aes(x = MAE)) + 
  geom_histogram(bins = 30, colour="black", fill = "#FDE725FF") +
  facet_wrap(~ Regression, scales = "free") +
  scale_x_continuous(breaks = seq(0, 11, by = 1)) +
  labs(title="Distribution of MAE", subtitle = "Random K-Fold and LOGO-CV",
       x="Mean Absolute Error", y="Count") +
  theme(plot.title = element_text(size = 15, face= "bold", color = "darkred"))

error_by_reg_and_fold %>%
  filter(str_detect(Regression, "LOGO")) %>%
  ggplot() +
  geom_sf(data = phlBoundary, fill = "white", color = "darkgrey") +
    geom_sf(aes(fill = MAE)) +
    facet_wrap(~Regression) +
    scale_colour_viridis(option = "plasma") +
    scale_fill_viridis(option = "plasma") +
    labs(title = "Errors by LOGO-CV Regression", caption="Figure 15") +
    theme(plot.title = element_text(size = 15, face= "bold", color = "darkred"))+
    theme(strip.text = element_text(size = 8))

# Table of MAE and Standard Deviation MAE

st_drop_geometry(error_by_reg_and_fold) %>%
  group_by(Regression) %>% 
    summarize(Mean_MAE = round(mean(MAE), 2),
              SD_MAE = round(sd(MAE), 2)) %>%
  kable(caption = "Table 1: MAE and standard deviation MAE by regression") %>%
    kable_styling("striped", full_width = F) %>%
    row_spec(2, color = "black", background = "#FDE725FF") %>%
    row_spec(4, color = "black", background = "#FDE725FF") 
Table 1: MAE and standard deviation MAE by regression
Regression Mean_MAE SD_MAE
Random k-fold CV: Just Risk Factors 0.58 0.58
Random k-fold CV: Spatial Process 0.54 0.53
Spatial LOGO-CV: Just Risk Factors 1.39 2.71
Spatial LOGO-CV: Spatial Process 0.98 1.83

The areas with some of the greatest mean absolute error are closer to center city, such as Fishtown, University City, and Graduate Hospital. Regions of Philadelphia close to the North, West, South, and along the Delaware river showed lower errors, both in terms of just risk factors and spatial process. There was higher error shown in the Spatial LOGO-CV process than the other validation methods. The random k-fold processes resulted in lower mean absolute error.

# Assuming 'error_by_reg_and_fold' contains the necessary columns and spatial data
# First, ensure that your data frame has the appropriate structure and unique row names

# Check for unique row names and reset if necessary
if(anyDuplicated(row.names(error_by_reg_and_fold))) {
  rownames(error_by_reg_and_fold) <- make.unique(as.character(row.names(error_by_reg_and_fold)))
}

# Create weights only for the selected regression type
neighborhood.weights <- error_by_reg_and_fold %>%
  filter(Regression == "Spatial LOGO-CV: Spatial Process") %>%
  st_as_sf() %>%
  group_by(cvID) %>%
  poly2nb(., queen = TRUE) %>%  # Corrected: Removed style and zero.policy
  nb2listw(., style = "W", zero.policy = TRUE)  # Correct usage of style and zero.policy

filter(error_by_reg_and_fold, str_detect(Regression, "LOGO"))  %>% 
    st_drop_geometry() %>%
    group_by(Regression) %>%
    summarize(Morans_I = moran.mc(abs(Mean_Error), neighborhood.weights, 
                                 nsim = 999, zero.policy = TRUE, 
                                 na.action=na.omit)[[1]],
              p_value = moran.mc(abs(Mean_Error), neighborhood.weights, 
                                 nsim = 999, zero.policy = TRUE, 
                                 na.action=na.omit)[[3]]) %>% 
  kable(caption = "Table 2: Moran's I on Errors by Regression") %>%
    kable_styling("striped", full_width = F) %>%
    row_spec(1, color = "black", background = "#FDE725FF") %>%
    row_spec(1, color = "black", background = "#FDE725FF") 

This displays the residuals of different regression models used to predict new construction permits, segmented by model type. From the plot, it’s evident that the distribution of residuals varies across the models, with some showing a tighter cluster around the zero line (indicating better prediction accuracy) and others displaying more spread, suggesting less precision.

reg.summary <- reg.summary %>%
  mutate(Residuals = countPermits - Prediction)

# residual plot

ggplot(reg.summary, aes(x = Prediction, y = Residuals)) +
  geom_point(alpha = 0.4) +  # Use alpha to adjust point transparency
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +  # Add a horizontal line at y = 0
  labs(title = "Residual Plot", x = "Predicted Values", y = "Residuals") +
  theme_minimal()

ggplot(reg.summary, aes(x = Prediction, y = Residuals, color = Regression)) +
  geom_point(alpha = 0.4) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  facet_wrap(~ Regression) +  # Separate plot for each regression type
  labs(title = "Residual Plot by Regression Type", x = "Predicted Values", y = "Residuals") +
  scale_color_viridis_d() +  # Corrected to use discrete color scale
  theme_minimal()

Kernel Density

The series of plots provided illustrate the spatial distribution of construction permits in Philadelphia using kernel density estimates with different search radii and a comparison of these estimates with spatial risk predictions. The first set of images shows kernel density maps for construction permits using three different search radii (1000 ft., 1500 ft., and 2000 ft.). As the search radius increases, the density map becomes smoother and broader, indicating a generalization in the concentration of construction activity across the city. This helps identify which areas are experiencing high volumes of construction relative to others, potentially pointing to hotspots of development or gentrification.

# demo of kernel width
permits_ppp <- as.ppp(st_coordinates(cnstPermits), W = st_bbox(final_net))
permits_KD.1000 <- density.ppp(permits_ppp, 1000)
permits_KD.1500 <- density.ppp(permits_ppp, 1500)
permits_KD.2000 <-density.ppp(permits_ppp, 2000)
permits_KD.df <- rbind(
  mutate(data.frame(rasterToPoints(mask(raster(permits_KD.1000), as(phillyNeighborhoods, 'Spatial')))), Legend = "1000 Ft."),
  mutate(data.frame(rasterToPoints(mask(raster(permits_KD.1500), as(phillyNeighborhoods, 'Spatial')))), Legend = "1500 Ft."),
  mutate(data.frame(rasterToPoints(mask(raster(permits_KD.2000), as(phillyNeighborhoods, 'Spatial')))), Legend = "2000 Ft.")) 

permits_KD.df$Legend <- factor(permits_KD.df$Legend, levels = c("1000 Ft.", "1500 Ft.", "2000 Ft."))

ggplot(data=permits_KD.df, aes(x=x, y=y)) +
  geom_raster(aes(fill=layer)) + 
  facet_wrap(~Legend) +
  coord_sf(crs=st_crs(final_net)) + 
  scale_fill_viridis(name="Density") +
  labs(title = "Kernel density with 3 different search radii") +
  theme_void()

#works but i dont think is needed unless there's one specific tihng we wanna look at 

as.data.frame(permits_KD.1000) %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(final_net)) %>%
  aggregate(., final_net, mean) %>%
   ggplot() +
     geom_sf(aes(fill=value)) +
     geom_sf(data = sample_n(cnstPermits, 1500), size = .5) +
     scale_fill_viridis(name = "Density") +
     labs(title = "Kernel density of Construction Permits") +
     theme_void()

### Comparing models

The plots below delve deeper by overlaying actual permit locations on the density estimates and comparing these against spatial risk predictions. These risk predictions categorize areas based on the predicted level of construction activity (from “Minimal” to “Very High” risk), allowing for a nuanced understanding of development intensity. These visualizations are crucial as they highlight areas where infrastructure and regulations might need to be reassessed due to rapid development. Specifically, areas classified as “Very High Risk” may require more attention to manage the impacts of gentrification, such as displacement and changes in the socio-economic fabric.

# Prediction by Kernel Density Model

permits_KD_sf_2022 <- as.data.frame(permits_KD.1500) %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(final_net)) %>%
  aggregate(., final_net, mean) %>%
  mutate(label = "Kernel Density Estimates",
         Risk_Level = ntile(value, 5),  # Change granularity of risk levels
         Risk_Level = case_when(
           Risk_Level == 5 ~ "Very High Risk",
           Risk_Level == 4 ~ "High Risk",
           Risk_Level == 3 ~ "Moderate Risk",
           Risk_Level == 2 ~ "Low Risk",
           TRUE ~ "Minimal Risk")) %>%
  cbind(
    aggregate(
      dplyr::select(cnstPermits_2022) %>% mutate(permitCount = 1), ., sum) %>%
      mutate(permitCount = replace_na(permitCount, 0))) %>%
  dplyr::select(label, Risk_Level, permitCount)

# Prediction by Risk Prediction Model

permits_KD_risk_sf_2022 <-
  reg.ss.spatialCV %>%
  mutate(label = "Spatial Risk Predictions",
         Risk_Level = ntile(Prediction, 5),
         Risk_Level = case_when(
           Risk_Level == 5 ~ "Very High Risk",
           Risk_Level == 4 ~ "High Risk",
           Risk_Level == 3 ~ "Moderate Risk",
           Risk_Level == 2 ~ "Low Risk",
           TRUE ~ "Minimal Risk")) %>%
   cbind(
    aggregate(
      dplyr::select(cnstPermits_2022) %>% mutate(permitCount = 1), ., sum) %>%
      mutate(permitCount = replace_na(permitCount, 0))) %>%
  dplyr::select(label, Risk_Level, permitCount)

unique(permits_KD_sf_2022$Risk_Level)  # Check for the 'permits_KD_sf' dataset
unique(permits_KD_risk_sf_2022$Risk_Level)  # Check for the 'permits_KD_risk_sf' dataset

summary(permits_KD_sf_2022$Risk_Level)
summary(permits_KD_risk_sf_2022$Risk_Level)

# Ensure 'option' and 'direction' are set to handle fewer categories
scale_fill_viridis_d(option = "plasma", direction = 1, begin = 0, end = 1, name = "Risk Level")

# Plotting comparison between both models

rbind(permits_KD_sf_2022, permits_KD_risk_sf_2022) %>%
  na.omit() %>%
  gather(Variable, Value, -label, -Risk_Level, -geometry) %>%
  ggplot() +
  geom_sf(aes(fill = Risk_Level), colour = NA) +
  geom_sf(data = sample_n(cnstPermits_2022, 1130), size = .5, colour = "black") +
  facet_wrap(~label, ) +
  scale_fill_viridis_d(option = "plasma",
                       name = 'Risk Level') + 
  labs(title="Comparison of Kernel Density and Risk Predictions",
       subtitle="Bottom Layer is 2022 Predicted Permit Counts.\nDots are observed 2022 Permit Counts.\n",
       caption = 'Figure 19') +
  mapTheme() +
  plotTheme()

The chart below reveals significant differences in the distribution of predicted permit counts between the two models. Spatial Risk Predictions consistently estimate higher rates of permits across all risk categories when compared to Kernel Density Estimates, with particularly stark contrasts in the “Very High Risk” and “Low Risk” categories. This suggests that the Spatial Risk Prediction model may be more sensitive to factors that predict higher construction activity, potentially providing a more dynamic or nuanced insight into the spatial distribution of construction risk compared to the more generalized Kernel Density approach.

# Comparison of predictions - Bar Plots

rbind(permits_KD_sf_2022, permits_KD_risk_sf_2022) %>%
  st_set_geometry(NULL) %>% 
  na.omit() %>%
  gather(Variable, Value, -label, -Risk_Level) %>%
  group_by(label, Risk_Level) %>%
  summarize(permitCount = sum(Value)) %>%
  ungroup() %>%
  group_by(label) %>%
  mutate(Rate_of_permit_count = permitCount / sum(permitCount)) %>%
  ggplot(aes(Risk_Level,Rate_of_permit_count)) +
  geom_bar(aes(fill=label), position="dodge", stat="identity") +
  scale_fill_viridis_d(option = "plasma",
                       name = 'Model') +
  labs(title = "Risk Prediction vs. Kernel Density",
       subtitle = 'Philadelphia, PA',
       caption = 'Figure 20',
       x = 'Risk Level',
       y = 'Rate of Permit Counts') +
  plotTheme()

Risk Mapping

Neighborhoods that may be at a high risk of gentrification by the proxy of new construction permits are mostly located near center city. Most of West Philadelphia, a large portion of North Philadelphia, and some parts of the Northwest are at risk of gentrification. The highest risk neighborhoods are Point Breeze, Fishtown, Rittenhouse, and Northern Liberties.

In addition to these areas, large swathes of West Philadelphia and significant portions of North Philadelphia, as well as some parts of the Northwest, are depicted as moderate to high risk. This extensive distribution suggests a broad impact of development that may influence housing markets, demographic shifts, and local economies across a considerable part of the city.

# Assigning Risk Categories for Predictions

permit_risk_sf_2022 <-
  filter(reg.summary, Regression == "Spatial LOGO-CV: Spatial Process") %>%
  mutate(label = "Spatial Risk Predictions",
         Risk_Level = ntile(Prediction, 5),
         Risk_Level = case_when(
           Risk_Level == 5 ~ "Very High Risk",
           Risk_Level == 4 ~ "High Risk",
           Risk_Level == 3 ~ "Moderate Risk",
           Risk_Level == 2 ~ "Low Risk",
           TRUE ~ "Minimal Risk"))


permit_risk_sf_2022 %>%
  gather(Variable, Value, -label, -Risk_Level, -geometry) %>%
  ggplot() +
    geom_sf(aes(fill = Risk_Level), colour = NA) +
    geom_sf(data=phillyNeighborhoods, fill = "transparent") +
    scale_fill_viridis(discrete = TRUE) +
    labs(title="Risk Predictions",
         subtitle="New Construction Permit Predictions",
         caption="Figure 20") +
    mapTheme()+
    plotTheme()

# Neighborhoods at highest risk of permitting construction
highest_risk_nhoods <-
  permit_risk_sf_2022 %>%
  dplyr::select(cvID, Risk_Level, countPermits) %>%
  dplyr::filter(., Risk_Level == "Very High Risk") %>%  # Adjusted to match the new risk category
  st_drop_geometry() %>%
  group_by(cvID) %>%
  summarise(Predicted_Permits = sum(countPermits)) %>%
  dplyr::filter(., Predicted_Permits > 52) %>%
  arrange(., desc(Predicted_Permits)) %>%
  rename(Neighborhood = cvID)

# Plotting the highest risk neighborhoods
ggplot(highest_risk_nhoods) +
  geom_bar(aes(x = reorder(Neighborhood, -Predicted_Permits), y = Predicted_Permits), fill = "#FE9900", stat = "identity", width = 0.5) +
  labs(x = "Neighborhood", y = "Predicted Permits", 
       title = "Neighborhoods with Very High Permitting Risk", subtitle = 'Philadelphia, PA', caption = "Figure 21") +
  theme(axis.text.x = element_text(size = 6, angle = 60, vjust = 0.7, hjust = 0.7),
        axis.text.y = element_text(size = 6),
        axis.title = element_text(size = 8), 
        plot.title = element_text(size = 9, face = "bold", color = "darkred"),
        plot.subtitle = element_text(size = 7, face = "italic", color = "black"),
        plot.margin = margin(0, 50, 8, 50, "pt"))

The implications of such widespread development are complex, potentially leading to increased property values and living costs, which could displace long-standing residents and alter the cultural fabric of these neighborhoods. The maps help identify where proactive measures might be necessary to mitigate the adverse effects of gentrification, such as promoting affordable housing initiatives and supporting local businesses to preserve neighborhood character and inclusivity.

Comparing to a different time

Let’s see how our model performed relative to KD on another year’s data. Comparing the spatial risk predictions and kernel density estimates from 2013 to 2022 in Philadelphia, there is a noticeable shift in risk distribution and the concentration of predicted permit counts. In 2013, the very high-risk categories were predominantly emphasized in the kernel density model, indicating a higher concentration of construction activity in a few areas. By 2022, the spatial risk predictions model shows a more distributed risk across the city, with a significant portion of the city classified under very high risk, suggesting an expansion in areas targeted for new construction. This shift could imply a broadening of development focus, potentially spreading gentrification pressures more widely across Philadelphia. This comparison highlights the dynamic nature of urban development and the increasing spread of high-risk areas over the years, necessitating updated and responsive planning and policy measures.

permits_KD_sf_2013 <- as.data.frame(permits_KD.1500) %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(final_net)) %>%
  aggregate(., final_net, mean) %>%
  mutate(label = "Kernel Density Estimates",
         Risk_Level = ntile(value, 5),  # Change granularity of risk levels
         Risk_Level = case_when(
           Risk_Level == 5 ~ "Very High Risk",
           Risk_Level == 4 ~ "High Risk",
           Risk_Level == 3 ~ "Moderate Risk",
           Risk_Level == 2 ~ "Low Risk",
           TRUE ~ "Minimal Risk")) %>%
  cbind(
    aggregate(
      dplyr::select(cnstPermits_2013) %>% mutate(permitCount = 1), ., sum) %>%
      mutate(permitCount = replace_na(permitCount, 0))) %>%
  dplyr::select(label, Risk_Level, permitCount)

# Prediction by Risk Prediction Model

permits_KD_risk_sf_2013 <-
  reg.ss.spatialCV %>%
  mutate(label = "Spatial Risk Predictions",
         Risk_Level = ntile(Prediction, 5),
         Risk_Level = case_when(
           Risk_Level == 5 ~ "Very High Risk",
           Risk_Level == 4 ~ "High Risk",
           Risk_Level == 3 ~ "Moderate Risk",
           Risk_Level == 2 ~ "Low Risk",
           TRUE ~ "Minimal Risk")) %>%
   cbind(
    aggregate(
      dplyr::select(cnstPermits_2013) %>% mutate(permitCount = 1), ., sum) %>%
      mutate(permitCount = replace_na(permitCount, 0))) %>%
  dplyr::select(label, Risk_Level, permitCount)

unique(permits_KD_sf_2013$Risk_Level)  # Check for the 'permits_KD_sf' dataset
unique(permits_KD_risk_sf_2013$Risk_Level)  # Check for the 'permits_KD_risk_sf' dataset

summary(permits_KD_sf_2013$Risk_Level)
summary(permits_KD_risk_sf_2013$Risk_Level)

# Ensure 'option' and 'direction' are set to handle fewer categories
scale_fill_viridis_d(option = "plasma", direction = 1, begin = 0, end = 1, name = "Risk Level")

# Plotting comparison between both models

rbind(permits_KD_sf_2013, permits_KD_risk_sf_2013) %>%
  na.omit() %>%
  gather(Variable, Value, -label, -Risk_Level, -geometry) %>%
  ggplot() +
  geom_sf(aes(fill = Risk_Level), colour = NA) +
  geom_sf(data = sample_n(cnstPermits_2013, 1130), size = .5, colour = "black") +
  facet_wrap(~label, ) +
  scale_fill_viridis_d(option = "plasma",
                       name = 'Risk Level') + 
  labs(title="Comparison of Kernel Density and Risk Predictions",
       subtitle="Bottom Layer is 2013 Predicted Permit Counts.\nDots are observed 2013 Permit Counts.\n",
       caption = 'Figure __') +
  mapTheme() +
  plotTheme()

rbind(permits_KD_risk_sf_2013, permits_KD_risk_sf_2022) %>%
  na.omit() %>%
  gather(Variable, Value, -label, -Risk_Level, -geometry) %>%
  ggplot() +
    geom_sf(aes(fill = Risk_Level), colour = NA) +
    geom_sf(data = sample_n(cnstPermits, 3000), size = .5, colour = "black") +
    facet_wrap(~label, ) +
    scale_fill_viridis(discrete = TRUE) +
    labs(title="Comparison of Kernel Density and Risk Predictions",
         subtitle="2013 Construction Permits Density; 2022 Construction Permits Density") +
    mapTheme()

rbind(permits_KD_sf_2013, permits_KD_risk_sf_2013) %>%
  st_drop_geometry() %>%
  na.omit() %>%
  gather(Variable, Value, -label, -Risk_Level) %>%
  group_by(label, Risk_Level) %>%
  summarize(countPermits = sum(Value)) %>%
  ungroup() %>%
  group_by(label) %>%
  mutate(Pcnt_cnst_permits = countPermits / sum(countPermits)) %>%
    ggplot(aes(Risk_Level,Pcnt_cnst_permits)) +
      geom_bar(aes(fill=label), position="dodge", stat="identity") +
      scale_fill_viridis(discrete = TRUE, name = "Model") +
      labs(title = "Risk prediction vs. Kernel density, 2013",
           y = "% of Test Set (per model)",
           x = "Risk Level") +
  theme_bw() +
      theme(axis.text.x = element_text(angle = 45, vjust = 0.5))

Conclusion

The model can be used to plan for future policy that can protect legacy residents and small businesses of neighborhoods that are at risk by identifying the neighborhoods where gentrification can occur. This model was built with respect to new construction, permitting, and demolition data, among other predictors. From this analysis, it was identified that many neighborhoods in the region surrounding Center City Philadelphia are at risk for gentrification, especially Point Breeze, Rittenhouse, and river-adjacent neighborhoods like East Kensington, Fishtown, and Northern Liberties.

Our work can be used to inform planning decisions that will be carried out by the Office of Innovation and Technology and the Smart Cities office. While it can be a useful tool, it should be used in tandem with other tools to predict and plan for gentrification in different neighborhoods. The staff at Marie Antoinette Predictions chose variables that were possibly predictors of gentrification, but we did not account for everything that could affect and become a predictor for gentrification. The existence and disappearance of civic assets like schools, libraries, green spaces, and public spaces could be markers of possible gentrification that are not tied to the data used in the model.

Additionally, there are different situations and nuances of displacement that cannot be captured in the data. Predicting the order and timeline displacement is a further challenge that OIT and the Smart Cities office could tackle. Direct displacement, or the phenomenon of when residents can no longer afford to live in their homes because of rising costs, would occur first with the proposals and new constructions and other types of permits8. Indirect displacement occurs when higher income residents move into an area when lower income residents move out. This can be captured through census data, in the median income or income bracket breakdown, to see the financial landscape of the neighborhood change. Finally, there is cultural displacement that occurs when civic assets change. Shops change to focus their customer base on new people, and it broadly feels like the neighborhood has transformed10. A model that can track a neighborhood using data throughout these phases is one that should be used by the city to identify gentrification.

LS0tCnRpdGxlOiAnRmluYWw6IE1lYXN1cmluZyBHZW50cmlmaWNhdGlvbicKYXV0aG9yOiAiS2FteWEgS2hhbmRlbHdhbCwgUmV2YXRoaSBNYWNoYW4sIENsYXVkaWEgU2NocmVpZXIiCmRhdGU6ICIwNS8xMy8yMDI0IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsICAKLS0tCgoqVE86KiBBa3NoYXkgTWFsaWssIENpdHkgb2YgUGhpbGFkZWxwaGlhIFNtYXJ0IENpdGllcyBEaXJlY3RvcgoqRlJPTToqIEthbXlhIEtoYW5kZWx3YWwsIFJldmF0aGkgTWFjaGFuLCBDbGF1ZGlhIFNjaHJlaWVyCipEQVRFOiogMTMgTWF5IDIwMjQKKlJFOiogUHJlZGljdGl2ZSBtb2RlbGluZyBmb3IgZ2VudHJpZmljYXRpb24gaW4gUGhpbGFkZWxwaGlhCgojIyBHZW50cmlmaWNhdGlvbiBpbiBQaGlsYWRlbHBoaWEgCgpQaGlsYWRlbHBoaWEgaGFzIGJlZW4gZXhwZXJpZW5jaW5nIGEgY29zdC1vZi1saXZpbmcgY3Jpc2lzIGZvciBkZWNhZGVzLiBGb3IgYSBjaXR5IGNvbW1vbmx5IHJlZmVycmVkIHRvIGFzIHRoZSBuYXRpb27igJlzIHBvb3Jlc3QgbGFyZ2VzdCBjaXR5LCB0aGVyZSBpcyBtdWNoIHdvcnJ5IGFib3V0IHRoZSBmdXR1cmUgb2YgbmV3IGRldmVsb3BtZW50cyBhbmQgaG91c2luZ14xXi4gUGhpbGFkZWxwaGlhIGhhcyB0aGUgaGlnaGVzdCBwb3ZlcnR5IHJhdGUgb3V0IG9mIHRoZSB0ZW4gbGFyZ2VzdCBjaXRpZXMgaW4gdGhlIFVTLiBJbiAyMDE4LCBhcm91bmQgMjMxLDAwMCBQaGlsYWRlbHBoaWEgaG91c2Vob2xkcyB3ZXJlIGNvc3QtYnVyZGVuZWQsIHdoaWNoIGlzIGRlZmluZWQgYnkgdGhlIFVTIERlcGFydG1lbnQgb2YgSG91c2luZyBhbmQgVXJiYW4gRGV2ZWxvcG1lbnQgYXMgYSBzaXR1YXRpb24gd2hlbiBhIGhvdXNlaG9sZCBzcGVuZHMgMzAlIG9yIG1vcmUgb2YgaXRzIGluY29tZSBvbiBob3VzaW5nIGNvc3RzLCBpbmNsdWRpbmcgcmVudCwgbW9ydGdhZ2UgcGF5bWVudHMsIHV0aWxpdGllcywgaW5zdXJhbmNlLCBhbmQgcHJvcGVydHkgdGF4ZXNeMl4uIFRoaXMgcHJvYmxlbSBpcyBtb3JlIHNldmVyZSBmb3IgcmVudGVycyDigJMgYW1vbmcgcmVudGVycyB3aXRoIGluY29tZXMgYmVsb3cgJDMwLDAwMCBwZXIgeWVhciwgODglIGFyZSBjb3N0LWJ1cmRlbmVkIGFuZCA2OCUgYXJlIHNldmVyZWx5IGNvc3QtYnVyZGVuZWQgKHNwZW5kaW5nIG1vcmUgdGhhbiA1MCUgb2YgaW5jb21lIG9uIGhvdXNpbmcpLiAgCgpXaXRoIGxvdHMgb2YgY29zdC1idXJkZW5lZCBob3VzZWhvbGRlcnMsIGluY2x1ZGluZyBob21lb3duZXJzIGFuZCByZW50ZXJzLCBpbiB0aGUgY2l0eSwgaXQgc2VlbXMgdGhhdCB0aGUgZmVhciAgb2YgZGlzcGxhY2VtZW50IGFuZCBnZW50cmlmaWNhdGlvbiBpbiBtYW55IG5laWdoYm9yaG9vZHMgaXMgY29tbW9uLiBIaXN0b3JpY2FsbHkgaGlnaCByZW50cyBhbmQgbW9ydGdhZ2UgcmF0ZXMsIGNvbWJpbmVkIHdpdGggdW5wcmVjZWRlbnRlZCBsZXZlbHMgb2YgbmV3IGRldmVsb3BtZW50cyBoYXZlIHNwdXJyZWQgZGlzY3Vzc2lvbiBhcm91bmQgZ2VudHJpZmljYXRpb24gaW4gdGhlIGNpdHleNF4uIEdlbnRyaWZpY2F0aW9uIGlzIGRlZmluZWQgYXMgYSB0eXBlIG9mIG5laWdoYm9yaG9vZCBjaGFuZ2Ugd2hlcmUgaGlnaGVyIGluY29tZSByZXNpZGVudHMgcmVwbGFjZSBsb3dlciBpbmNvbWUgb25lcyDigJMgaG91c2luZyBjb3N0cyByaXNlLCBwcm9wZXJ0eSB2YWx1ZXMgcmlzZSwgYW5kIHR5cGljYWxseSwgbG9uZyB0ZXJtIHJlc2lkZW50cyBnZXQgZGlzcGxhY2VkXjVeLiBHZW50cmlmaWNhdGlvbiB0b2RheSBpcyBpbmZvcm1lZCBieSBhIGxlZ2FjeSBvZiBkaXNwbGFjZW1lbnQgdGhhdCB3YXMgc3B1cnJlZCBieSDigJ1yZWRldmVsb3BtZW504oCdIGFuZCDigJ11cmJhbiByZW5ld2Fs4oCdIHBsYW5zLiBQaGlsYWRlbHBoaWEgd2FzIGhpdCBlc3BlY2lhbGx5IGhhcmQgYnkgdGhpcyBkaXNwbGFjZW1lbnQgaW4gV2VzdCBQaGlsYWRlbHBoaWEgd2l0aCB1bml2ZXJzaXR5IGV4cGFuc2lvbiBwbGFucyB0aHJvdWdob3V0IHRoZSBzZWNvbmQgaGFsZiBvZiB0aGUgMjB0aCBjZW50dXJ5LgoKSW4gYnJvYWRlciBkaXNjdXNzaW9ucyBhYm91dCBnZW50cmlmaWNhdGlvbiBvbmxpbmUgYW5kIGluIG1lZGlhLCBnZW50cmlmaWNhdGlvbiBjYW4gc2VlbSBsaWtlIHNvbWV0aGluZyB0aGF0IG9jY3VycyB3aGVuIGEgYnJld2VyeSByZXBsYWNlcyBhIGNvcm5lciBzdG9yZSwgb3IgYSBsdXh1cnkgZ3ltIHJlcGxhY2VzIGEgc2luZ2xlLWZhbWlseSBob21lLiBHZW50cmlmaWNhdGlvbiBjYW4gYmUgbWFya2VkIGJ5IG1hbnkgdGhpbmdzLCBhbmQgdGhlIHNpZ25zIGNhbiB2YXJ5IGZvciBkaWZmZXJlbnQgcGVvcGxlIHRoYXQgYSBuZWlnaGJvcmhvb2QgaXMgYmVpbmcgZ2VudHJpZmllZC4gIFRoZXJlIGhhcyBldmVuIGJlZW4gbmV3IHRlY2hub2xvZ3kgbGlrZSBBSSBtb2RlbHMgY3JlYXRlZCB0byBpZGVudGlmeSBzaWducyBvZiBnZW50cmlmaWNhdGlvbiBmcm9tIHRlbXBvcmFsIG1hcHMgbGlrZSBHb29nbGUgU3RyZWV0IFZpZXdeNl4uIFdoaWxlIHRoZSBzaWducyBvZiBnZW50cmlmaWNhdGlvbiBjYW4gYmUgdmlzaWJsZSwgdGhleSBtYXkgYmUgaGFyZCB0byB0cmFjayB0aHJvdWdoIGRhdGEuIFJlc2VhcmNoZXJzIHJlbHkgb24gZGVtb2dyYXBoaWMgZGF0YSB0byBtYWtlIGNvbmNsdXNpb25zIGFib3V0IHRoZSB0eXBlcyBvZiBjaGFuZ2UgdGhhdCB0aGUgbmVpZ2hib3Job29kIGlzIGV4cGVyaWVuY2luZywgbGlrZSByYWNpYWwgY2hhbmdlcywgYWdlIGNoYW5nZXMsIGFuZCBpbmNvbWUgY2hhbmdlcy4KCiMjIERhdGEgZXhwbG9yYXRpb24KClRoaXMgcHJvamVjdCBzZWVrcyB0byBjcmVhdGUgYSBwcmVkaWN0aXZlIG1vZGVsIGZvciBnZW50cmlmaWNhdGlvbiB0aGF0IGNhbiBiZSB1c2VkIGluIHBsYW5uaW5nIHByYWN0aWNlIHRvIGlkZW50aWZ5IGFyZWFzIGF0IHJpc2sgZm9yIGdlbnRyaWZpY2F0aW9uIG9yIGlkZW50aWZ5IGFyZWFzIHVuZGVyZ29pbmcgZ2VudHJpZmljYXRpb24uIFRoaXMgbW9kZWwgdXNlcyBmYWN0b3JzIHRoYXQgdGhlIHRlYW0gYXQgTWFyaWUgQW50b2luZXR0ZSBQcmVkaWN0aW9ucyBoYXZlIHJlc2VhcmNoZWQgYW5kIGJlbGlldmUgYXJlIHJlbGlhYmxlIG1hcmtlcnMgZm9yIGdlbnRyaWZpY2F0aW9uLiBUaGUgdmFyaWFibGVzIGluIG91ciBtb2RlbCBhcmUgY29uc3RydWN0aW9uIHBlcm1pdHMsIHZhY2FudCBwcm9wZXJ0aWVzLCBhZmZvcmRhYmxlIGhvdXNpbmcgbG9jYXRpb25zLCBncmVlbiBzcGFjZXMsIGFuZCBkZW1vbGl0aW9ucy4gVGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBmcm9tIFBoaWxhZGVscGhpYeKAmXMgb3BlbiBkYXRhIHBvcnRhbF43Xi4gQWZ0ZXIgZG93bmxvYWRpbmcgdGhlIHJlbGV2YW50IGRhdGEsIGl0IHdhcyBjbGVhbmVkIHRvIGVuc3VyZSB0aGF0IHZhcmlhYmxlcyB3ZXJlIGNsZWFyIGFuZCB0cmFuc2ZlcmFibGUuIAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSkKCgppZighcmVxdWlyZShwYWNtYW4pKXtpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKTsgbGlicmFyeShwYWNtYW4pfQpwX2xvYWQoc2YsIHRpZHl2ZXJzZSwgdGlkeWNlbnN1cywgUlNvY3JhdGEsIHZpcmlkaXMsIHNwYXRzdGF0LCByYXN0ZXIsIHNwZGVwLCBGTk4sIGdyaWQsIGdyaWRFeHRyYSwga25pdHIsIGthYmxlRXh0cmEsIGNsYXNzSW50LCBSQ29sb3JCcmV3ZXIsIGdnY29ycnBsb3QsIGdncGxvdDIsIHZpcmlkaXMsIGRwbHlyKQoKIyBmdW5jdGlvbnMKcm9vdC5kaXIgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VyYmFuU3BhdGlhbC9QdWJsaWMtUG9saWN5LUFuYWx5dGljcy1MYW5kaW5nL21hc3Rlci9EQVRBLyIKc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdXJiYW5TcGF0aWFsL1B1YmxpYy1Qb2xpY3ktQW5hbHl0aWNzLUxhbmRpbmcvbWFzdGVyL2Z1bmN0aW9ucy5yIikKCmBgYAoKCmBgYHtyIG1hcCB0aGVtZSwgcmVzdWx0cyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CgptYXBUaGVtZSA8LSBmdW5jdGlvbihiYXNlX3NpemUgPSAxMikgewogIHRoZW1lKAogICAgdGV4dCA9IGVsZW1lbnRfdGV4dCggY29sb3IgPSAiYmxhY2siKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LGNvbG91ciA9ICJibGFjayIpLAogICAgcGxvdC5jYXB0aW9uPWVsZW1lbnRfdGV4dChoanVzdD0wKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0yKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpKQp9CgpwbG90VGhlbWUgPC0gZnVuY3Rpb24oYmFzZV9zaXplID0gMTIpIHsKICB0aGVtZSgKICAgIHRleHQgPSBlbGVtZW50X3RleHQoIGNvbG9yID0gImJsYWNrIiksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImRhcmtncmVlbiIsIHNpemU9MTUsIGZhY2U9ImJvbGQiKSwKICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiksCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3Q9MCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoImdyZXk4MCIsIHNpemUgPSAwLjEpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTIpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk4MCIsIGNvbG9yID0gIndoaXRlIiksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIGZhY2UgPSAiaXRhbGljIiksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBmYWNlID0gIml0YWxpYyIpLAogICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkKICApCn0KYGBgCgojIyMgUmVhZCBpbiBEYXRhIGZyb20gUGhpbGFkZWxwaGlhCgpgYGB7ciByZWFkX2RhdGEsIHJlc3VsdHMgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIHBlcm1pdHMKcGhsUGVybWl0cyA8LSAKICBzdF9yZWFkKCJodHRwczovL3BobC5jYXJ0by5jb20vYXBpL3YyL3NxbD9xPVNFTEVDVCsqK0ZST00rcGVybWl0cytXSEVSRStwZXJtaXRpc3N1ZWRhdGUrPj0rJzIwMTMtMDEtMDEnK0FORCtwZXJtaXRpc3N1ZWRhdGUrPCsnMjAyMi0xMi0zMScmZmlsZW5hbWU9cGVybWl0cyZmb3JtYXQ9Z2VvanNvbiZza2lwZmllbGRzPWNhcnRvZGJfaWQiKSAlPiUgCiAgICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyNzI4JyklPiUKICBtdXRhdGUoTGVnZW5kID0gIlBoaWxhZGVscGhpYSBQZXJtaXRzIiklPiUKICBtdXRhdGUoWWVhciA9IGFzLmludGVnZXIoZm9ybWF0KHBlcm1pdGlzc3VlZGF0ZSwgIiVZIikpKSAKCiMjIDIuIFBoaWxhZGVscGhpYSBCb3VuZGFyaWVzCnBobEJvdW5kYXJ5IDwtIAogIHN0X3JlYWQoImh0dHA6Ly9kYXRhLnBobC5vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzQwNWVjM2RhOTQyZDRlMjA4NjlkNGUxNDQ5YTJiZTQ4XzAuZ2VvanNvbiIpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDI3MjgnKQoKI2R1bm5vIGlmIHdlIG5lZWQgdG8gZmlzaG5ldCB0aGlzCnBobEZpc2huZXQgPC0gCiAgc3RfbWFrZV9ncmlkKHBobEJvdW5kYXJ5LAogICAgICAgICAgICAgICBjZWxsc2l6ZSA9IDUwMCwgCiAgICAgICAgICAgICAgIHNxdWFyZSA9IFRSVUUpICU+JQogIC5bcGhsQm91bmRhcnldICU+JSAKICBzdF9zZigpICU+JQogIG11dGF0ZSh1bmlxdWVJRCA9IHJvd25hbWVzKC4pKQoKIyMgMy4gUGhpbGFkZWxwaGlhIE5laWdoYm9yaG9vZHMgLSBpc24ndCBsb2FkaW5nCnBoaWxseU5laWdoYm9yaG9vZHMgPC0KICBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vb3BlbmRhdGFwaGlsbHkvb3Blbi1nZW8tZGF0YS9tYXN0ZXIvcGhpbGFkZWxwaGlhLW5laWdoYm9yaG9vZHMvcGhpbGFkZWxwaGlhLW5laWdoYm9yaG9vZHMuZ2VvanNvbiIpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDI3MjgnKSAlPiUKICBzdF90cmFuc2Zvcm0oc3RfY3JzKHBobEZpc2huZXQpKSAKCiMjIDQuIFZhY2FudCBQcm9wZXJ0eQp2YWNhbnRCdWlsZGluZyA8LQogIHN0X3JlYWQoJ2h0dHBzOi8vb3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy9mN2VkNjgyOTNjNWU0MGQ1OGYxZGU5Yzg0MzVjM2U4NF8wLmdlb2pzb24nKSAlPiUgCiAgbmEub21pdCgpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDI3MjgnKSAlPiUKICBzdF90cmFuc2Zvcm0oc3RfY3JzKHBobEZpc2huZXQpKSAlPiUKICBtdXRhdGUoTGVnZW5kID0gIlZhY2FudCBCdWlsZGluZ3MiKQoKIyMgNS4gQWZmb3JkYWJsZSBIb3VzaW5nCmFmZm9yZGFibGVIb3VzaW5nIDwtCiAgc3RfcmVhZCgnaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzL2NhODk0NDE5MGI2MDRiMmFhZTdlZmYxN2M4ZGQ5ZWY1XzAuZ2VvanNvbicpICU+JSAKICBmaWx0ZXIoRklTQ0FMX1lFQVJfQ09NUExFVEUgPj0gIjIwMTIiKSAlPiUKICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyNzI4JykgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhwaGxGaXNobmV0KSkgJT4lCiAgbXV0YXRlKExlZ2VuZCA9ICJBZmZvcmRhYmxlIEhvdXNpbmciKQoKCiMjIDcuIEdyZWVuIFNwYWNlcwpncmVlblNwYWNlIDwtCiAgc3RfcmVhZCgnaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzL2Q1MjQ0NTE2MGFiMTQzODBhNjczZTU4NDkyMDNlYjY0XzAuZ2VvanNvbicpICU+JSAKICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyNzI4JykgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhwaGxGaXNobmV0KSkgJT4lCiAgbXV0YXRlKExlZ2VuZCA9ICJHcmVlbiBTcGFjZXMiKQoKCiMjIDEwLiBEZW1vbGl0aW9uIERhdGEKYnVpbGRpbmdEZW1vbGl0aW9uIDwtCiAgc3RfcmVhZCgnaHR0cHM6Ly9waGwuY2FydG8uY29tL2FwaS92Mi9zcWw/cT1TRUxFQ1QrKitGUk9NK2RlbW9saXRpb25zJmZpbGVuYW1lPWRlbW9saXRpb25zJmZvcm1hdD1nZW9qc29uJnNraXBmaWVsZHM9Y2FydG9kYl9pZCcpICU+JSAKICBtdXRhdGUoeWVhciA9IHN1YnN0cihzdGFydF9kYXRlLDEsNCkpICU+JQogIGZpbHRlcih5ZWFyID09ICcyMDIyJykgJT4lCiAgc3RfdHJhbnNmb3JtKCdFU1JJOjEwMjcyOCcpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMocGhsRmlzaG5ldCkpICU+JQogIG11dGF0ZShMZWdlbmQgPSAiQnVpbGRpbmcgRGVtb2xpdGlvbiIpIAoKIyMgMTEuIENlbnN1cyBkYXRhIC0gQUNTIDIwMjEKdHJhY3RzMjIgPC0gZ2V0X2FjcygKICBnZW9ncmFwaHkgPSAidHJhY3QiLAogIHZhcmlhYmxlcyA9IGMoCiAgICAiQjAxMDAzXzAwMSIsICAgIyBUb3RhbCBQb3B1bGF0aW9uCiAgICAiQjE5MDEzXzAwMSIsICAgIyBNZWRpYW4gSG91c2Vob2xkIEluY29tZQogICAgIkIyNTAwOF8wMDIiLCAgICMgT3duZXItT2NjdXBpZWQgVW5pdHMKICAgICJCMjUwMDhfMDAzIiwgICAjIFJlbnRlci1PY2N1cGllZCBVbml0cwogICAgIkIyNTAwOF8wMDFFIiwgICMgVG90YWwgUG9wdWxhdGlvbiBpbiBIb3VzaW5nIFVuaXRzCiAgICAiQjE1MDAzXzAyMiIsICAgIyBFZHVjYXRpb25hbCBBdHRhaW5tZW50OiBCYWNoZWxvcidzIERlZ3JlZQogICAgIkIwNjAxMl8wMDJFIiwgICMgUG9wdWxhdGlvbiBCZWxvdyB0aGUgUG92ZXJ0eSBMZXZlbAogICAgIkIwMjAwMV8wMDIiLCAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBXaGl0ZSBBbG9uZQogICAgIkIwMjAwMV8wMDMiLCAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBCbGFjayBvciBBZnJpY2FuIEFtZXJpY2FuIEFsb25lCiAgICAiQjI3MDExXzAwOEUiICAjIFBvcHVsYXRpb24gVW5lbXBsb3llZAogICksCiAgeWVhciA9IDIwMjIsCiAgc3RhdGUgPSAiUEEiLAogIGNvdW50eSA9ICJQaGlsYWRlbHBoaWEiLAogIGdlb21ldHJ5ID0gVFJVRSwKICBvdXRwdXQgPSAid2lkZSIKKSU+JQogIGRwbHlyOjpzZWxlY3QoLU5BTUUsIC1lbmRzX3dpdGgoIk0iKSkgJT4lCiAgcmVuYW1lKHRvdGFsUG9wID0gQjAxMDAzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUb3RhbCBQb3B1bGF0aW9uCiAgICAgICAgIG1lZEhISW5jID0gQjE5MDEzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZWRpYW4gSG91c2Vob2xkIEluY29tZQogICAgICAgICB0b3RhbFVuaXQgPSBCMjUwMDhfMDAxRSwgICAgICAgICAgICAgICAgICAgICAgICAgICMgVG90YWwgUG9wdWxhdGlvbiBpbiBIb3VzaW5nIFVuaXRzCiAgICAgICAgIG93bmVyT2NjdXBpZWQgPSBCMjUwMDhfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgIyBPd25lci1PY2N1cGllZCBVbml0cwogICAgICAgICByZW50ZXJPY2N1cGllZCA9IEIyNTAwOF8wMDNFLCAgICAgICAgICAgICAgICAgICAgICMgUmVudGVyLU9jY3VwaWVkIFVuaXRzCiAgICAgICAgIGJhY2hEZWdyZWUgPSBCMTUwMDNfMDIyRSwgICAgICAgICAgICAgICAgICAgICMgRWR1Y2F0aW9uYWwgQXR0YWlubWVudDogQmFjaGVsb3IncyBEZWdyZWUKICAgICAgICAgdG90YWxQb3YgPSBCMDYwMTJfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICMgUG9wdWxhdGlvbiBCZWxvdyB0aGUgUG92ZXJ0eSBMZXZlbAogICAgICAgICB0b3RhbFVuZW1wbG95ID0gQjI3MDExXzAwOEUsICAgICAgICAgICAgICAgICAgIyBQb3B1bGF0aW9uIFVuZW1wbG95ZWQKICAgICAgICAgd2hpdGVBbG9uZSA9IEIwMjAwMV8wMDJFLCAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IFdoaXRlIEFsb25lCiAgICAgICAgIGJsYWNrQWxvbmUgPSBCMDIwMDFfMDAzRSAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IEJsYWNrIG9yIEFmcmljYW4gQW1lcmljYW4gQWxvbmUKICAgICAgICAgKQoKIyMjIDExLjEuIFRyYW5zZm9ybSB0aGUgZGF0YSB0byBFU1JJOjEwMjcyOCBwcm9qZWN0aW9uCgp0cmFjdHMyMiA8LSB0cmFjdHMyMiAlPiUgc3RfdHJhbnNmb3JtKHN0X2NycyhwaGxGaXNobmV0KSkKCiMjIyAxMS4yIENyZWF0ZSBuZXcgdmFyaWFibGVzCgp0cmFjdHMyMiA8LSB0cmFjdHMyMiAlPiUKICBtdXRhdGUocGN0V2hpdGUgPSBpZmVsc2UodG90YWxQb3AgPiAwLCB3aGl0ZUFsb25lIC8gdG90YWxQb3AgKiAxMDAsMCksCiAgICAgICAgIHBjdEJsYWNrID0gaWZlbHNlKHRvdGFsUG9wID4gMCwgYmxhY2tBbG9uZSAvIHRvdGFsUG9wICogMTAwLDApLAogICAgICAgICBwY3RQb3YgPSBpZmVsc2UodG90YWxQb3AgPiAwLCB0b3RhbFBvdiAvIHRvdGFsUG9wICoxMDAsIDApLAogICAgICAgICBwY3RVbmVtcGxveSA9IGlmZWxzZSh0b3RhbFBvcCA+IDAsIHRvdGFsVW5lbXBsb3kgLyB0b3RhbFBvcCAqMTAwLCAwKSwKICAgICAgICAgcGN0QmFjaCA9IGlmZWxzZSh0b3RhbFBvcCA+IDAsIGJhY2hEZWdyZWUgLyB0b3RhbFBvcCAqMTAwLCAwKSwKICAgICAgICAgcGN0T3duZXJPY2N1cGllZCA9IGlmZWxzZSh0b3RhbFBvcCA+IDAsIG93bmVyT2NjdXBpZWQgLyB0b3RhbFVuaXQgKjEwMCwgMCksCiAgICAgICAgIHBjdFJlbnRlck9jY3VwaWVkID0gaWZlbHNlKHRvdGFsUG9wID4gMCwgcmVudGVyT2NjdXBpZWQgLyB0b3RhbFVuaXQgKjEwMCwgMCkKICAgICAgICAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KC13aGl0ZUFsb25lLCAtYmxhY2tBbG9uZSwgLXRvdGFsUG92ICwtdG90YWxVbmVtcGxveSwtb3duZXJPY2N1cGllZCwgLXJlbnRlck9jY3VwaWVkLCAtdG90YWxVbml0LCAtYmFjaERlZ3JlZSwgLUdFT0lEKSAlPiUKICBzdF90cmFuc2Zvcm0oc3RfY3JzKHBobEZpc2huZXQpKSAKCiMjIyAxMS4zIE9yZ2FuaXplIGludG8gZGF0YXNldHMgLSB3aGF0IGlzIHRoaXMgd2hvbGUgb3JnYW5pemF0aW9uIHBhcnQgZm9yPz8KCnRyYWN0czIyLm1lZEhISW5jIDwtIHRyYWN0czIyICU+JQogIGRwbHlyOjpzZWxlY3QobWVkSEhJbmMpICU+JQogIHJlbmFtZShMZWdlbmQgPSBtZWRISEluYykKCnRyYWN0czIyLnBjdFdoaXRlIDwtIHRyYWN0czIyICU+JQogIGRwbHlyOjpzZWxlY3QocGN0V2hpdGUpJT4lCiAgcmVuYW1lKExlZ2VuZCA9IHBjdFdoaXRlKQoKdHJhY3RzMjIucGN0QmxhY2sgPC0gdHJhY3RzMjIgJT4lCiAgZHBseXI6OnNlbGVjdChwY3RCbGFjayklPiUKICByZW5hbWUoTGVnZW5kID0gcGN0QmxhY2spCgp0cmFjdHMyMi5wY3RQb3YgPC0gdHJhY3RzMjIgJT4lCiAgZHBseXI6OnNlbGVjdChwY3RQb3YpJT4lCiAgcmVuYW1lKExlZ2VuZCA9IHBjdFBvdikKCnRyYWN0czIyLnBjdFVuZW1wbG95IDwtIHRyYWN0czIyICU+JQogIGRwbHlyOjpzZWxlY3QocGN0VW5lbXBsb3kpJT4lCiAgcmVuYW1lKExlZ2VuZCA9IHBjdFVuZW1wbG95KQoKdHJhY3RzMjIucGN0QmFjaCA8LSB0cmFjdHMyMiAlPiUKICAgZHBseXI6OnNlbGVjdChwY3RCYWNoKSU+JQogICByZW5hbWUoTGVnZW5kID0gcGN0QmFjaCkKCnRyYWN0czIyLnBjdE93bmVyT2NjdXBpZWQgPC0gdHJhY3RzMjIgJT4lCiAgZHBseXI6OnNlbGVjdChwY3RPd25lck9jY3VwaWVkKSU+JQogIHJlbmFtZShMZWdlbmQgPSBwY3RPd25lck9jY3VwaWVkKQoKdHJhY3RzMjIucGN0UmVudGVyT2NjdXBpZWQgPC0gdHJhY3RzMjIgJT4lCiAgZHBseXI6OnNlbGVjdChwY3RSZW50ZXJPY2N1cGllZCklPiUKICByZW5hbWUoTGVnZW5kID0gcGN0UmVudGVyT2NjdXBpZWQpCmBgYAoKIyMjIERhdGEgVmlzdWFsaXNhdGlvbgoKVGhlIGRhdGEgd2FzIHZpc3VhbGl6ZWQgdXNpbmcgbWFwcGluZyB0b29scy4gVGhlIGFyZWEgZnJvbSBhcm91bmQgQ2VudGVyIENpdHkgdG8gbG93ZXIgTm9ydGggUGhpbGFkZWxwaGlhIGFuZCBLZW5zaW5ndG9uIGhhcyB0aGUgZGVuc2VzdCBhbW91bnQgb2YgY29uc3RydWN0aW9uIHBlcm1pdHMgYW5kIGRlbW9saXRpb24gcGVybWl0cy4gVGhlcmUgaXMgYSBoaWdoIGRlbnNpdHkgb2YgdmFjYW50IGJ1aWxkaW5ncyBpbiBXZXN0IGFuZCBOb3J0aCBQaGlsYWRlbHBoaWEuCgpgYGB7ciBwb2xpY2UgZGF0YSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CgojQ2F0ZWdvcml6aW5nIHRoZSBwZXJtaXRzIGZvciBjb25zdHJ1Y3Rpb24gYW5kIGRlbW9saXRpb24KcGhsUGVybWl0cyA8LSBwaGxQZXJtaXRzICU+JQogIG11dGF0ZShuZXdUeXBlID0gY2FzZV93aGVuKHBlcm1pdHR5cGUgPT0gIkJVSUxESU5HIiB8IHBlcm1pdHR5cGUgPT0gIkJQX05FV0NOU1QiICB+ICdDT05TVFJVQ1RJT04gUEVSTUlUJywKICBwZXJtaXR0eXBlID09ICJERU1PTElUSU9OIiB8IHBlcm1pdHR5cGUgPT0gIkJQX0RFTU8iIH4gJ0RFTU9MSVRJT04gUEVSTUlUJykpCgoKY25zdFBlcm1pdHMgPC0gcGhsUGVybWl0cyAlPiUKICBmaWx0ZXIobmV3VHlwZSA9PSAnQ09OU1RSVUNUSU9OIFBFUk1JVCcpCgpkZW1vUGVybWl0cyA8LSBwaGxQZXJtaXRzICU+JQogIGZpbHRlcihuZXdUeXBlID09ICdERU1PTElUSU9OIFBFUk1JVCcpCgpjbnN0UGVybWl0c18yMDIyIDwtIGNuc3RQZXJtaXRzJT4lCiAgZmlsdGVyKFllYXI9PTIwMjIpCgpjbnN0UGVybWl0c18yMDEzIDwtIGNuc3RQZXJtaXRzJT4lCiAgZmlsdGVyKFllYXIgPT0gMjAxMykKYGBgCgpUaGUgcGxvdHMgYmVsb3cgc2hvdyBob3cgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBwZXJtaXRzIGFyZSBsb2NhdGVkIGFsbCBvdmVyIFBoaWxhZGVscGhpYSwgbG9va2luZyBwcmltYXJpbHkgYXQgdGhlIHRoZSBwZXJtaXRzIHRoYXQgZmFsbCB1bmRlciBjb25zdHJ1Y3Rpb24gb3Igem9uaW5nLiBXZSBhbHNvIGxvb2sgYXQgaG93IG90aGVyIHZhcmlvdXMgY2hvc2VuIGRlbW9ncmFwaGljIGFuZCBzb2Npby1lY29ub21pYyBmYWN0b3JzIGFyZSBtYXBwZWQgb3V0IGFjcm9zcyB0aGUgY2l0eSB0byBjb21wYXJlIHRoZW0gYWdhaW5zdCB3aGVyZSB0aGVyZSBpcyB0aGUgaGlnaGVzdCBkZW5zaXR5IG9mIHBlcm1pdHMuIENvbnN0cnVjdGlvbiBwZXJtaXRzIGNhbiBiZSBzZWVuIGluIGhpZ2hlciBjb25jZW50cmF0aW9ucyBpbiB0aGUgYXJlYXMgd2hlcmUgdGhlcmUgaXMgYSBoaWdoZXIgcGVyY2VudGFnZSBvZiBpbmRpdmlkdWFscyB3aXRoIGJhY2hlbG9yJ3MgZGVncmVlcyBhbmQgaGlnaGVyIHVuZW1wbG95bWVudC4KCmBgYHtyIHBsb3QgdmFyaWFibGVzLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00LCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KCiMgUGxvdCAxOiBtYXAgb2YgYWxsIGNvbnN0cnVjdGlvbiBhbmQgZGVtbyBwZXJtaXRzIGlzc3VlZCBiL3cgMjAxMyBhbmQgMjAyMgpnZ3Bsb3QoKSArIAogICAgZ2VvbV9zZihkYXRhID0gcGhsQm91bmRhcnksIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArICAKICAgIGdlb21fc2YoZGF0YSA9IHBobFBlcm1pdHMsIGFlcyhjb2xvdXIgPSBuZXdUeXBlKSwgc2l6ZSA9IDAuNSwgc2hvdy5sZWdlbmQgPSAicG9pbnQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm1hZ2VudGEiLCAibGlnaHRncmVlbiIpKSArCiAgbGFicyh0aXRsZSA9ICJNYWpvciBQZXJtaXRzIElzc3VlZCwgMjAxMy0yMiBpbiBQaGlsYWRlbHBoaWEiLAogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgMSIpICsKICAgIHRoZW1lX3ZvaWQoKSAgCgoKICAjIFBsb3QgMiArIDM6IE1hcHBlZCBwb2ludHMgYW5kIERlbnNpdHkgbWFwIG9mIGNvbnN0cnVjdGlvbiBwZXJtaXRzIGlzc3VlZApnZ3Bsb3QoKSArIAogIGdlb21fc2YoZGF0YSA9IHBobEJvdW5kYXJ5LCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKyAgCiAgZ2VvbV9zZihkYXRhID0gY25zdFBlcm1pdHMsIGFlcyhjb2xvciA9ICJtYWdlbnRhIiksIHNpemUgPSAwLjUpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoZ3VpZGUgPSAibGVnZW5kIiwgbmFtZSA9IE5VTEwsIGxhYmVscyA9ICJDb25zdHJ1Y3Rpb24gUGVybWl0cyIpICsKICBsYWJzKHRpdGxlID0gIkNvbnN0cnVjdGlvbiBQZXJtaXRzIElzc3VlZCwgMjAxMy0yMiBpbiBQaGlsYWRlbHBoaWEiLAogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgMiIpICsKICB0aGVtZV92b2lkKCkgCgoKICBnZ3Bsb3QoKSArIAogICAgZ2VvbV9zZihkYXRhID0gcGhsQm91bmRhcnksIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArICAKICAgIHN0YXRfZGVuc2l0eTJkKGRhdGEgPSBkYXRhLmZyYW1lKHN0X2Nvb3JkaW5hdGVzKGNuc3RQZXJtaXRzKSksICAKICAgICAgICAgICAgICAgICAgIGFlcyhYLCBZLCBmaWxsID0gLi5sZXZlbC4uLCBhbHBoYSA9IC4ubGV2ZWwuLiksICAKICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjAxLCBiaW5zID0gNDAsIGdlb20gPSAncG9seWdvbicpICsgIAogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0icm9ja2V0IikgKyAgCiAgICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgb2YgQ29uc3RydWN0aW9uIFBlcm1pdHMiKSArICAKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgIAoKCgojUGxvdCA0ICsgNTogTWFwcGVkIHBvaW50cyBhbmQgRGVuc2l0eSBtYXAgb2YgZGVtb2xpdGlvbiBwZXJtaXRzIGlzc3VlZApnZ3Bsb3QoKSArIAogICAgZ2VvbV9zZihkYXRhID0gcGhsQm91bmRhcnksIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArICAKICAgIGdlb21fc2YoZGF0YSA9IGRlbW9QZXJtaXRzLCBhZXMoY29sb3VyID0gImxpZ2h0Z3JlZW4iKSwgc2l6ZSA9IDAuNSkgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KGd1aWRlID0gImxlZ2VuZCIsIG5hbWUgPSBOVUxMLCBsYWJlbHMgPSAiRGVtb2xpdGlvbiBQZXJtaXRzIikgKwogIGxhYnModGl0bGUgPSAiRGVtb2xpdGlvbiBQZXJtaXRzIElzc3VlZCwgMjAxMy0yMiBpbiBQaGlsYWRlbHBoaWEiLAogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNCIpICsKICAgIHRoZW1lX3ZvaWQoKQoKZ2dwbG90KCkgKyAKICAgIGdlb21fc2YoZGF0YSA9IHBobEJvdW5kYXJ5LCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKyAgCiAgICBzdGF0X2RlbnNpdHkyZChkYXRhID0gZGF0YS5mcmFtZShzdF9jb29yZGluYXRlcyhkZW1vUGVybWl0cykpLCAgCiAgICAgICAgICAgICAgICAgICBhZXMoWCwgWSwgZmlsbCA9IC4ubGV2ZWwuLiwgYWxwaGEgPSAuLmxldmVsLi4pLCAgCiAgICAgICAgICAgICAgICAgICBzaXplID0gMC4wMSwgYmlucyA9IDQwLCBnZW9tID0gJ3BvbHlnb24nKSArICAKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJyb2NrZXQiKSArICAKICAgIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjAwLCAwLjM1KSwgZ3VpZGUgPSBGQUxTRSkgKyAgCiAgICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgb2YgQ29uc3RydWN0aW9uIFBlcm1pdHMiKSArICAKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgIAoKCiMgcGxvdCA2OiBhZmZvcmRhYmxlIGhvdXNpbmcKZ2dwbG90KCkgKyAKICBnZW9tX3NmKGRhdGEgPSBwaGxCb3VuZGFyeSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsgIAogIGdlb21fc2YoZGF0YSA9IGFmZm9yZGFibGVIb3VzaW5nLCBhZXMoY29sb3IgPSAib3JhbmdlIiksIHNpemUgPSAwLjUpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoZ3VpZGUgPSAibGVnZW5kIiwgbmFtZSA9IE5VTEwsIGxhYmVscyA9ICJBZmZvcmRhYmxlIEhvdXNpbmcgVW5pdHMiKSArCiAgbGFicyh0aXRsZSA9ICJBZmZvcmRhYmxlIEhvdXNpbmcgRGV2ZWxvcG1lbnRzLCAyMDEzLTIyIGluIFBoaWxhZGVscGhpYSIsCiAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA2IikgKwogIHRoZW1lX3ZvaWQoKSAKCiNwbG90NzogCmdncGxvdCgpICsgCiAgZ2VvbV9zZihkYXRhID0gcGhsQm91bmRhcnksIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArICAKICBnZW9tX3NmKGRhdGEgPSBncmVlblNwYWNlLCBhZXMoY29sb3IgPSAiZGFya2dyZWVuIiksIHNpemUgPSAwLjUpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoZ3VpZGUgPSAibGVnZW5kIiwgbmFtZSA9IE5VTEwsIGxhYmVscyA9ICJHcmVlbiBTcGFjZXMiKSArCiAgbGFicyh0aXRsZSA9ICJHcmVlbiBTcGFjZXMsIDIwMTMtMjIgaW4gUGhpbGFkZWxwaGlhIiwKICAgICAgIGNhcHRpb24gPSAiRmlndXJlIDciKSArCiAgdGhlbWVfdm9pZCgpIAoKCiNwbG90OCBhbmQgOTogdmFjYW50IGxhbmQgcG9pbnQgYW5kIGRlbnNpdHkgbWFwcwpnZ3Bsb3QoKSArIAogICAgZ2VvbV9zZihkYXRhID0gcGhsQm91bmRhcnksIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArICAKICAgIGdlb21fc2YoZGF0YSA9IHZhY2FudEJ1aWxkaW5nLCBhZXMoY29sb3IgPSAnZGFya3JlZCcpLCBzaXplID0gMC41LCBzaG93LmxlZ2VuZCA9ICJwb2ludCIpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eShndWlkZSA9ICJsZWdlbmQiLCBuYW1lID0gTlVMTCwgbGFiZWxzID0gIlZhY2FudCBCdWlsZGluZ3MiKSArCiAgbGFicyh0aXRsZSA9ICJTdXNwZWN0ZWQgVmFjYW50IEJ1aWxkaW5ncyBpbiBQaGlsYWRlbHBoaWEiLAogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgOCIpICsKICAgIHRoZW1lX3ZvaWQoKQoKZ2dwbG90KCkgKyAKICAgIGdlb21fc2YoZGF0YSA9IHBobEJvdW5kYXJ5LCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKyAgCiAgICBzdGF0X2RlbnNpdHkyZChkYXRhID0gZGF0YS5mcmFtZShzdF9jb29yZGluYXRlcyh2YWNhbnRCdWlsZGluZykpLCAgCiAgICAgICAgICAgICAgICAgICBhZXMoWCwgWSwgZmlsbCA9IC4ubGV2ZWwuLiwgYWxwaGEgPSAuLmxldmVsLi4pLCAgCiAgICAgICAgICAgICAgICAgICBzaXplID0gMC4wMSwgYmlucyA9IDQwLCBnZW9tID0gJ3BvbHlnb24nKSArICAKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJyb2NrZXQiKSArICAKICAgIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjAwLCAwLjM1KSwgZ3VpZGUgPSBGQUxTRSkgKyAKICAgIGxhYnModGl0bGUgPSAiRGVuc2l0eSBvZiBWYWNhbnQgQnVsZGluZ3MiKSArICAKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgIAoKCgojcGxvdDEwOiBkZW1vZ3JhcGhpYwpnZ3Bsb3QoKSsKICAgIGdlb21fc2YoZGF0YT10cmFjdHMyMiwgYWVzKGNvbG9yPU5BLCBmaWxsPXBjdFVuZW1wbG95KSkrCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzKCkrCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpKwogICAgZ2VvbV9zZihkYXRhID0gY25zdFBlcm1pdHMsIGFlcyhjb2xvcj0ieWVsbG93IikgLAogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjEsIGFscGhhPTAuMykgKwogICAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgICBsYWJzKHRpdGxlPSIlIFVuZW1wbG95bWVudCBhcm91bmQgUGVybWl0cyBJc3N1ZWQiKSsKICAgIG1hcFRoZW1lKCkrdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKQoKZ2dwbG90KCkrCiAgICBnZW9tX3NmKGRhdGE9dHJhY3RzMjIsIGFlcyhjb2xvcj1OQSwgZmlsbD1wY3RCYWNoKSkrCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzKCkrCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMoKSsKICAgIGdlb21fc2YoZGF0YSA9IGNuc3RQZXJtaXRzLCBhZXMoY29sb3I9InllbGxvdyIpICwKICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC4xLCBhbHBoYT0wLjMpICsKICAgIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogICAgbGFicyh0aXRsZT0iJSBQb3B1bGF0aW9uIHdpdGggQmFjaGVsb3IncyBEZWdyZWUgYXJvdW5kIFBlcm1pdHMgSXNzdWVkIikKYGBgCiMjIyBGaXNobmV0CgpIZXJlIHdlIGNvbnN0cnVjdCBhIGdlb3NwYXRpYWwgZGF0YXNldCB0byBleGFtaW5lIGhvdyB0aGUgcmlzayBvZiBuZXcgZGV2ZWxvcG1lbnQgaXMgc3ByZWFkIHRocm91Z2hvdXQgUGhpbGFkZWxwaGlhLiBUbyBkbyB0aGlzIHdlIGNyZWF0ZWQgYSBmaXNobmV0IHdoaWNoIGlzIGEgY29udGlub3VzIGdyaWQgcGF0dGVybiBjb25zaXN0aW5nIG9mIGNlbGxzIG1lYXN1cmluZyA1MDB4NTAwIGZlZXQuIFRoaXMgZ3JpZCBhbGxvd3MgdXMgdG8gY29uY2VwdHVhbGl6ZSBkZXZlbG9wbWVudCByaXNrLCByZXByZXNlbnRlZCBieSB0aGUgcHJlc2VuY2Ugb2YgbmV3IGNvbnN0cnVjdGlvbiBwZXJtaXRzLiBUaGUgZm9sbG93aW5nIHBsb3QgZGVwaWN0cyB0aGUgY291bnQgb2YgbmV3IGNvbnN0cnVjdGlvbiBwZXJtaXRzIGFjcm9zcyB0aGUgZmlzaG5ldCwgd2l0aCBncmlkIGNlbGxzIGRlcGljdGVkIGluIHllbGxvdyBpbmRpY2F0aW5nIHRoZSBoaWdoZXN0IHBlcm1pdCBjb3VudHMuCgpgYGB7ciBmaXNobmV0LCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KCiMgQXR0YWNoaW5nIGRhdGFzZXRzIG9uIHNwYXRpYWwgZmFjdG9ycyB0byBGaXNobmV0CgojIyAxLiBFeHRyYWN0aW5nIGdlb21ldHJ5IGZvciBzcGF0aWFsIGZhY3RvcnMKCgphZmZvcmRhYmxlSG91c2luZ3MgPC0gYWZmb3JkYWJsZUhvdXNpbmcgJT4lCiAgZHBseXI6OnNlbGVjdChnZW9tZXRyeSwgTGVnZW5kKQoKdmFjYW50QnVpbGRpbmdzIDwtIHZhY2FudEJ1aWxkaW5nICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VvbWV0cnksIExlZ2VuZCkKCmdyZWVuU3BhY2VzIDwtIGdyZWVuU3BhY2UgJT4lCiAgZHBseXI6OnNlbGVjdChnZW9tZXRyeSwgTGVnZW5kKQoKYnVpbGRpbmdEZW1vbGl0aW9ucyA8LSBidWlsZGluZ0RlbW9saXRpb24gJT4lCiAgZHBseXI6OnNlbGVjdChnZW9tZXRyeSwgTGVnZW5kKQoKCiMjIDIuIENyZWF0aW5nIGZpc2huZXQgb2Ygc3BhdGlhbCBmYWN0b3IgdmFyaWFibGVzIAoKdmFyc19uZXQgPC0gCiAgcmJpbmQoYWZmb3JkYWJsZUhvdXNpbmdzLCB2YWNhbnRCdWlsZGluZ3MsCiAgICAgICAgZ3JlZW5TcGFjZXMsIGJ1aWxkaW5nRGVtb2xpdGlvbnMpICU+JQogIHN0X2pvaW4oLiwgcGhsRmlzaG5ldCwgam9pbj1zdF93aXRoaW4pICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBncm91cF9ieSh1bmlxdWVJRCwgTGVnZW5kKSAlPiUKICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQogIGZ1bGxfam9pbihwaGxGaXNobmV0LCBieSA9ICJ1bmlxdWVJRCIpICU+JQogIHNwcmVhZChMZWdlbmQsIGNvdW50LCBmaWxsPTApICU+JQogIHN0X3NmKCkgJT4lCiAgbmEub21pdCgpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1gPE5BPmApICU+JQogIHVuZ3JvdXAoKQoKCmNuc3RQZXJtaXRzIDwtIHN0X3RyYW5zZm9ybShjbnN0UGVybWl0cywgc3RfY3JzKHBobEZpc2huZXQpKQoKY29uc3RydWN0aW9uX25ldCA8LSAKICBkcGx5cjo6c2VsZWN0KGNuc3RQZXJtaXRzKSAlPiUgCiAgbXV0YXRlKGNvdW50UGVybWl0cyA9IDEpICU+JSAKICBhZ2dyZWdhdGUoLiwgcGhsRmlzaG5ldCwgc3VtKSAlPiUKICBtdXRhdGUoY291bnRQZXJtaXRzID0gcmVwbGFjZV9uYShjb3VudFBlcm1pdHMsIDApLAogICAgICAgICB1bmlxdWVJRCA9IHJvd25hbWVzKC4pLAogICAgICAgICBjdklEID0gc2FtcGxlKHJvdW5kKG5yb3cocGhsRmlzaG5ldCkgLyAyNCksIAogICAgICAgICAgICAgICAgICAgICAgIHNpemU9bnJvdyhwaGxGaXNobmV0KSwgcmVwbGFjZSA9IFRSVUUpKQoKIyB2aXN1YWxpc2luZyBwZXJtaXRzIGpvaW5lZCB0byBmaXNobmV0CgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gY29uc3RydWN0aW9uX25ldCwgYWVzKGZpbGwgPSBjb3VudFBlcm1pdHMpLCBjb2xvciA9IE5BKSArCiAgZ2VvbV9zZihkYXRhID0gc3RfYm91bmRhcnkocGhsRmlzaG5ldCksIGNvbG9yID0gImRhcmtyZWQiLCBsd2QgPSAuMDQsIGFscGhhPS4yKSArICMgQWRkIGJvdW5kYXJpZXMgaW4gcmVkCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdDb25zdHJ1Y3Rpb24gQ291bnRzJykgKwogIGxhYnModGl0bGUgPSAiQ29uc3RydWN0aW9uIFBlcm1pdHMgSm9pbmVkIHRvIEZpc2huZXQiLAogICAgICAgc3VidGl0bGUgPSAnUGhpbGFkZWxwaGlhJykgKyBtYXBUaGVtZSgpICsgcGxvdFRoZW1lKCkKYGBgCiMjIyBOZWFyZXN0IE5laWdoYm91cgoKVGhpcyBjb2RlIHNlZ21lbnQgY3JlYXRlcyBhIG5lYXJlc3QgbmVpZ2hib3IgZmVhdHVyZSBmb3IgZGlmZmVyZW50IGNhdGVnb3JpZXMsIGluY2x1ZGluZyB2YWNhbnQgYnVpbGRpbmdzLCBhZmZvcmRhYmxlIGhvdXNpbmcsIGdyZWVuIHNwYWNlcywgYW5kIGJ1aWxkaW5nIGRlbW9saXRpb25zLiBGb3IgZWFjaCBjYXRlZ29yeSwgaXQgZmlyc3QgbWFwcyB0aGUgbmVhcmVzdCBmZWF0dXJlIGZyb20gYSBnaXZlbiBkYXRhc2V0IHRvIGFub3RoZXIgZGF0YXNldCwgY29udmVydHMgdGhlIGRhdGFzZXRzIHRvIGByc2dlb2AgZ2VvbWV0cmllcywgY2FsY3VsYXRlcyB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlIGJldHdlZW4gZWFjaCBvYnNlcnZhdGlvbiBpbiBvbmUgZGF0YXNldCBhbmQgaXRzIG5lYXJlc3QgbmVpZ2hib3IgaW4gdGhlIG90aGVyIGRhdGFzZXQuIEZpbmFsbHksIHRoZSBjYWxjdWxhdGVkIGRpc3RhbmNlcyBhcmUgYXBwZW5kZWQgYXMgbmV3IHZhcmlhYmxlcyB0byB0aGUgb3JpZ2luYWwgZGF0YXNldCBmb3IgZWFjaCBjYXRlZ29yeSwgc3VjaCBhcyBgZGlzdF92YWNhbnRCdWlsZGluZ2AsIGBkaXN0X2FmZm9yZGFibGVIb3VzaW5nYCwgYGRpc3RfZ3JlZW5TcGFjZWAsIGFuZCBgZGlzdF9idWlsZGluZ0RlbW9saXRpb25gLiBFc3NlbnRpYWxseSB0aGlzIHByb2Nlc3MgdHVybnMgZWFjaCBpbmRpdmlkdWFsIGdyaWQgY2VsbCBpbnRvIGEgY2VudHJvaWQgcG9pbnQgYW5kIHRoZW4gbWVhc3VyZXMgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIGVhY2ggdG8gZmluZCB0aGUgbmVhcmVzdCByaXNrIGZhY3RvciBwb2ludC4gCgpUaGlzIHNwYXRpYWwgYW5hbHlzaXMgaXMgY3J1Y2lhbCBmb3IgdW5kZXJzdGFuZGluZyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGRpZmZlcmVudCB1cmJhbiBmZWF0dXJlcyBhbmQgdGhlaXIgcHJveGltaXR5IHRvIG9uZSBhbm90aGVyLiBCeSBjYWxjdWxhdGluZyB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdmFyaWFibGVzIGxpa2UgdmFjYW50IGJ1aWxkaW5ncywgYWZmb3JkYWJsZSBob3VzaW5nLCBncmVlbiBzcGFjZXMsIGFuZCBidWlsZGluZyBkZW1vbGl0aW9ucywgaXQgcHJvdmlkZXMgaW5zaWdodHMgaW50byB0aGUgc3BhdGlhbCBkaXN0cmlidXRpb24gYW5kIHBvdGVudGlhbCBpbnRlcmFjdGlvbnMgYW1vbmcgdGhlc2UgZmVhdHVyZXMuCgpgYGB7ciBrbm4sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKIyMgMS4yLiBWYWNhbnQgQnVpbGRpbmdzCgojIyMgTWFwcGluZyBuZWFyZXN0IGZlYXR1cmUKCm5lYXJlc3RfdmFjYW50QnVpbGRpbmcgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZSh2YXJzX25ldCwgdmFjYW50QnVpbGRpbmcpCgojIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzCgp4IDwtIHJzZ2VvOjphc19yc2dlbyh2YXJzX25ldCkKeSA8LSByc2dlbzo6YXNfcnNnZW8odmFjYW50QnVpbGRpbmcpCgojIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UKCnZhcnNfbmV0JGRpc3RfdmFjYW50QnVpbGRpbmcgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfdmFjYW50QnVpbGRpbmddKQoKCiMjIDEuMy4gQWZmb3JkYWJsZSBIb3VzaW5nCgojIyMgTWFwcGluZyBuZWFyZXN0IGZlYXR1cmUKCm5lYXJlc3RfYWZmb3JkYWJsZUhvdXNpbmcgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZSh2YXJzX25ldCwgYWZmb3JkYWJsZUhvdXNpbmcpCgojIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzCgp4IDwtIHJzZ2VvOjphc19yc2dlbyh2YXJzX25ldCkKeSA8LSByc2dlbzo6YXNfcnNnZW8oYWZmb3JkYWJsZUhvdXNpbmcpCgojIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UKCnZhcnNfbmV0JGRpc3RfYWZmb3JkYWJsZUhvdXNpbmcgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfYWZmb3JkYWJsZUhvdXNpbmddKQoKCiMjIDEuNC4gR3JlZW4gU3BhY2VzCgojIyMgTWFwcGluZyBuZWFyZXN0IGZlYXR1cmUKCm5lYXJlc3RfZ3JlZW5TcGFjZSA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKHZhcnNfbmV0LCBncmVlblNwYWNlKQoKIyMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcwoKeCA8LSByc2dlbzo6YXNfcnNnZW8odmFyc19uZXQpCnkgPC0gcnNnZW86OmFzX3JzZ2VvKGdyZWVuU3BhY2UpCgojIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UKCnZhcnNfbmV0JGRpc3RfZ3JlZW5TcGFjZSA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9ncmVlblNwYWNlXSkKCiMjIDEuOC4gQnVpbGRpbmcgRGVtb2xpdGlvbnMKCiMjIyBNYXBwaW5nIG5lYXJlc3QgZmVhdHVyZQoKbmVhcmVzdF9idWlsZGluZ0RlbW9saXRpb24gPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZSh2YXJzX25ldCwgYnVpbGRpbmdEZW1vbGl0aW9uKQoKIyMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcwoKeCA8LSByc2dlbzo6YXNfcnNnZW8odmFyc19uZXQpCnkgPC0gcnNnZW86OmFzX3JzZ2VvKGJ1aWxkaW5nRGVtb2xpdGlvbikKCiMjIyBDYWxjdWxhdGluZyBkaXN0YW5jZQoKdmFyc19uZXQkZGlzdF9idWlsZGluZ0RlbW9saXRpb24gPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfYnVpbGRpbmdEZW1vbGl0aW9uXSkKCgpgYGAKCgpgYGB7ciB2aXpOTiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgMi4gVmlzdWFsaXppbmcgbmVhcmVzdCBkaXN0YW5jZSBmb3Igc3BhdGlhbCBmYWN0b3JzIG9uIEZpc2huZXQKCiMjIDIuMS4gVmlzdWFsaXppbmcgdGhlIG5lYXJlc3QgdGhyZWUgZmVhdHVyZXMKCnZhcnNfbmV0Lmxvbmcubm4gPC0gCiAgZHBseXI6OnNlbGVjdCh2YXJzX25ldCwgc3RhcnRzX3dpdGgoImRpc3QiKSkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCB2YWx1ZSwgLWdlb21ldHJ5KQoKdmFycyA8LSB1bmlxdWUodmFyc19uZXQubG9uZy5ubiRWYXJpYWJsZSkKbWFwTGlzdCA8LSBsaXN0KCkKCmZvcihpIGluIHZhcnMpewogIG1hcExpc3RbW2ldXSA8LSAKICAgIGdncGxvdCgpICsKICAgIGdlb21fc2YoZGF0YSA9IGZpbHRlcih2YXJzX25ldC5sb25nLm5uLCBWYXJpYWJsZSA9PSBpKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIgIikgKwogICAgbGFicyh0aXRsZT1pKSArCiAgICBtYXBUaGVtZSgpKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIikpCiAgfQoKYm90dG9tQ2FwdGlvbiA8LSB0ZXh0R3JvYigiRmlndXJlIDgiLCBncCA9IGdwYXIoaGp1c3QgPSAwKSkKCmRvLmNhbGwoZ3JpZC5hcnJhbmdlLCBjKGxpc3QoZ3JvYnMgPSBtYXBMaXN0LCBuY29sID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIlNwYXRpYWwgRmFjdG9yczogTmVhcmVzdCBOZWlnaGJvciBEaXN0YW5jZSBmb3IgUGVybWl0cyBJc3N1ZWRcbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE1LCBmb250ZmFjZSA9ICJib2xkIiwgY29sID0gImRhcmtyZWQiKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbSA9IGJvdHRvbUNhcHRpb24pKSkKCmBgYAoKIyMjIFNwYXRpYWwgSm9pbnMKCkhlcmUgd2Ugd2FudCB0byBqb2luIHRoZSBjZW5zdXMgZGF0YSB0byBvdXIgZmlzaG5ldCBzbyB3ZSBjYW4gYWxzbyBtYXAgb3VyIGRlbW9ncmFwaGljIHZhcmlhYmxlcyBpbiBhIHNpbWlsYXIgZmFzaGlvbiwgYW5kIGFsc28ganVzdCBzdGFuZGFyZGl6ZSBvdXIgbW9kZSBvZiBhbmFseXNpcyBhY3Jvc3MgYWxsIG91ciBkYXRhLgoKYGBge3Igam9pbiBjZW5zdXMgZGF0YSB0byBmaXNobmV0LCByZXN1bHRzID0gJ2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBKb2luaW5nIENlbnN1cyBEYXRhIHRvIEZpc2huZXQKCnRyYWN0czIyIDwtIHRyYWN0czIyICU+JQogIGZpbHRlcih0b3RhbFBvcD4wKQoKdmFyc19uZXQgPC0KICB2YXJzX25ldCU+JQogIHN0X2NlbnRyb2lkKCklPiUKICBzdF9qb2luKHRyYWN0czIyKQoKCnZhcnNfbmV0IDwtIHZhcnNfbmV0ICU+JSBtdXRhdGVfYWxsKH5yZXBsYWNlKC4sIGlzLm5hKC4pLCAwKSkKCiMgUGVyZm9ybSBTcGF0aWFsIEpvaW4gb2YgdmFyaWFibGVzIHdpdGggcGVybWl0cwoKZmluYWxfbmV0IDwtCiAgbGVmdF9qb2luKGNvbnN0cnVjdGlvbl9uZXQsIHN0X2Ryb3BfZ2VvbWV0cnkodmFyc19uZXQpLCBieT0idW5pcXVlSUQiKSAjIHRoaXMgb25lIGRvZXNuJ3Qgd29yayBzbyB0aGUgbGFzdCBvbmUgd29uJ3QgZWl0aGVyCgojIEZpbmFsIE5ldAoKCmZpbmFsX25ldCA8LQogIHN0X2NlbnRyb2lkKGZpbmFsX25ldCkgJT4lIAogICAgc3Rfam9pbihkcGx5cjo6c2VsZWN0KHBoaWxseU5laWdoYm9yaG9vZHMsIG5hbWUpLCBieSA9ICJ1bmlxdWVJRCIpICU+JSAKICAgICAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogICAgICBsZWZ0X2pvaW4oZHBseXI6OnNlbGVjdChmaW5hbF9uZXQsIGdlb21ldHJ5LCB1bmlxdWVJRCkpICU+JSAgCiAgICAgIHN0X3NmKCkgJT4lCiAgbmEub21pdCgpCmBgYAoKCmBgYHtyIGZpbmFsIG5ldCwgcmVzdWx0cyA9ICdoaWRlJywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMjIGdlbmVyYXRlcyB3YXJuaW5ncyBmcm9tIFBST0ogaXNzdWVzCiMjIHtzcGRlcH0gdG8gbWFrZSBwb2x5Z29uIHRvIG5laWdoYm9yaG9vZHMuLi4gCmZpbmFsX25ldC5uYiA8LSBwb2x5Mm5iKGFzX1NwYXRpYWwoZmluYWxfbmV0KSwgcXVlZW49VFJVRSkKIyMgLi4uIGFuZCBuZWlnaGJvcmhvb2RzIHRvIGxpc3Qgb2Ygd2VpZ3RocwpmaW5hbF9uZXQud2VpZ2h0cyA8LSBuYjJsaXN0dyhmaW5hbF9uZXQubmIsIHN0eWxlPSJXIiwgemVyby5wb2xpY3k9VFJVRSkKCiMgcHJpbnQoZmluYWxfbmV0LndlaWdodHMsIHplcm8ucG9saWN5PVRSVUUpCmBgYAoKIyMgTW9kZWxpbmcgUHJvY2VzcwoKIyMjIExvY2FsIE1vcmFuJ3MgSQoKSW5jb3Jwb3JhdGluZyBMb2NhbCBNb3JhbidzIEkgYW5hbHlzaXMgaW50byB0aGUgc3BhdGlhbCBtb2RlbGluZyBwcm9jZXNzIGlzIHBpdm90YWwgZm9yIHVuZGVyc3RhbmRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb25zdHJ1Y3Rpb24gcGVybWl0cyBhY3Jvc3MgdXJiYW4gYXJlYXMuIExvY2FsIE1vcmFuJ3MgSSBzZXJ2ZXMgYXMgYSB2aXRhbCB0b29sLCBwcm92aWRpbmcgaW5zaWdodHMgaW50byB0aGUgc3BhdGlhbCBjbHVzdGVyaW5nIG9yIGRpc3BlcnNpb24gb2YgcGVybWl0IGlzc3VhbmNlLCB0aGVyZWJ5IGVuaGFuY2luZyB0aGUgbW9kZWwncyBjYXBhY2l0eSB0byBpZGVudGlmeSBzaWduaWZpY2FudCBzcGF0aWFsIHBhdHRlcm5zIGluIHVyYmFuIGRldmVsb3BtZW50LiBCeSBpbnRlZ3JhdGluZyBMb2NhbCBNb3JhbidzIEkgc3RhdGlzdGljcyB3aXRoIG90aGVyIHNwYXRpYWwgZmFjdG9ycywgc3VjaCBhcyBuZWFyZXN0IG5laWdoYm9yIGRpc3RhbmNlcyBhbmQgZ2VvZ3JhcGhpYyBmZWF0dXJlcywgdGhlIG1vZGVsIGdhaW5zIGEgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSB1bmRlcmx5aW5nIHNwYXRpYWwgZHluYW1pY3MgaW5mbHVlbmNpbmcgY29uc3RydWN0aW9uIHBlcm1pdCBkaXN0cmlidXRpb25zLCB0aGVyZWJ5IGZhY2lsaXRhdGluZyBpbmZvcm1lZCB1cmJhbiBwbGFubmluZyBhbmQgcG9saWN5IGRlY2lzaW9ucy5JbiB0aGUgY29udGV4dCBvZiB0aGlzIG1vZGVsLCBMb2NhbCBNb3JhbidzIEkgaGVscHMgaWRlbnRpZnkgc2lnbmlmaWNhbnQgc3BhdGlhbCBwYXR0ZXJucyBpbiBjb25zdHJ1Y3Rpb24gcGVybWl0IGlzc3VhbmNlLCBpbmRpY2F0aW5nIGFyZWFzIHdpdGggaGlnaCBvciBsb3cgY29uY2VudHJhdGlvbnMgb2YgcGVybWl0cy4gCgpJbml0aWFsbHksIHRoZSBsb2NhbCBNb3JhbidzIEkgdmFsdWVzIGFyZSBjb21wdXRlZCBmb3IgdGhlIHBlcm1pdCBjb3VudHMgdXNpbmcgc3BhdGlhbCB3ZWlnaHRzLiBUaGVuLCB0aGVzZSByZXN1bHRzIGFyZSBqb2luZWQgd2l0aCB0aGUgc3BhdGlhbCBkYXRhLCBhbmQgc2lnbmlmaWNhbnQgaG90c3BvdHMgYXJlIGlkZW50aWZpZWQgYmFzZWQgb24gYSBzaWduaWZpY2FuY2UgdGhyZXNob2xkIG9mIDAuMDUuIFRoZSBvdXRwdXQgaXMgb3JnYW5pemVkIGludG8gYSBsb25nIGZvcm1hdCBmb3IgcGxvdHRpbmcsIHdoZXJlIGVhY2ggdmFyaWFibGUncyBMb2NhbCBNb3JhbidzIEkgc3RhdGlzdGljcyBhcmUgdmlzdWFsaXplZCBhcyBzcGF0aWFsIG1hcHMuIFRoZXNlIG1hcHMgZGVwaWN0IHRoZSBzcGF0aWFsIGNsdXN0ZXJpbmcgcGF0dGVybnMgb2YgcGVybWl0IGlzc3VhbmNlLCBpbmRpY2F0aW5nIGFyZWFzIG9mIHNpZ25pZmljYW50IGNsdXN0ZXJpbmcgb3IgZGlzcGVyc2lvbi4gCgpgYGB7ciBsb2NhbCBtb3JhbiwgcmVzdWx0cyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMjIHNlZSA/bG9jYWxtb3Jhbgpsb2NhbF9tb3JhbnMgPC0gbG9jYWxtb3JhbihmaW5hbF9uZXQkY291bnRQZXJtaXRzLCBmaW5hbF9uZXQud2VpZ2h0cywgemVyby5wb2xpY3k9VFJVRSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKIyBqb2luIGxvY2FsIE1vcmFuJ3MgSSByZXN1bHRzIHRvIGZpc2huZXQKZmluYWxfbmV0LmxvY2FsTW9yYW5zIDwtIAogIGNiaW5kKGxvY2FsX21vcmFucywgYXMuZGF0YS5mcmFtZShmaW5hbF9uZXQpKSAlPiUgCiAgc3Rfc2YoKSAlPiUKICBkcGx5cjo6c2VsZWN0KFBlcm1pdF9Db3VudCA9IGNvdW50UGVybWl0cywgCiAgICAgICAgICAgICAgICBMb2NhbF9Nb3JhbnNfSSA9IElpLCAKICAgICAgICAgICAgICAgIFBfVmFsdWUgPSBgUHIoeiAhPSBFKElpKSlgKSAlPiUKICBtdXRhdGUoU2lnbmlmaWNhbnRfSG90c3BvdHMgPSBpZmVsc2UoKFBfVmFsdWUgPD0gMC4wNSksIDEsIDApKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpCiAgCmBgYAoKCmBgYHtyIGxvY2FsIG1vcmFucyBpLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CgojIyBUaGlzIGlzIGp1c3QgZm9yIHBsb3R0aW5nCnZhcnMgPC0gdW5pcXVlKGZpbmFsX25ldC5sb2NhbE1vcmFucyRWYXJpYWJsZSkKdmFyTGlzdCA8LSBsaXN0KCkKCmZvcihpIGluIHZhcnMpewogIHZhckxpc3RbW2ldXSA8LSAKICAgIGdncGxvdCgpICsKICAgICAgZ2VvbV9zZihkYXRhID0gZmlsdGVyKGZpbmFsX25ldC5sb2NhbE1vcmFucywgVmFyaWFibGUgPT0gaSksIAogICAgICAgICAgICAgIGFlcyhmaWxsID0gVmFsdWUpLCBjb2xvdXI9TkEpICsKICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWU9IiIpICsKICAgICAgbGFicyh0aXRsZT1pKSArCiAgICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIil9Cgpkby5jYWxsKGdyaWQuYXJyYW5nZSxjKHZhckxpc3QsIG5jb2wgPSA0LCB0b3AgPSAiTG9jYWwgTW9yYW5zIEkgU3RhdGlzdGljcywgQ29uc3RydWN0aW9uIFBlcm1pdHMiKSkKc2F2ZVJEUyhsb2NhbF9tb3JhbnMsIGZpbGUgPSAibG9jYWxfbW9yYW5zLnJkcyIpCmBgYAoKIyMjIERpc3RhbmNlIHRvIEhvdHNwb3RzCgpTaG9ydGVyIGRpc3RhbmNlcyBiZXR3ZWVuIHBlcm1pdHMgaW5kaWNhdGUgYXJlYXMgd2l0aCBoaWdoZXIgY29uc3RydWN0aW9uIGRlbnNpdHksIHdoaWNoIGNhbiBiZSBhc3NvY2lhdGVkIHdpdGggcmFwaWQgdXJiYW4gZGV2ZWxvcG1lbnQgb3IgZ2VudHJpZmljYXRpb24uCgpgYGB7ciBsbWlfaG90c3BvdCxyZXN1bHRzID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KCmxvY2FsX21vcmFucyA8LSByZWFkUkRTKCJsb2NhbF9tb3JhbnMucmRzIikKCmZpbmFsX25ldCA8LSBmaW5hbF9uZXQgJT4lCiAgbXV0YXRlKHBlcm1pdC5pc1NpZyA9IGlmZWxzZShsb2NhbF9tb3JhbnNbLDVdIDw9IDAuMDAwMDAwMSwxLDApKSAlPiUKICBtdXRhdGUocGVybWl0LmlzU2lnLmRpc3QgPSBubl9mdW5jdGlvbihzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChmaW5hbF9uZXQpKSwgc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoZmlsdGVyKGZpbmFsX25ldCwgcGVybWl0LmlzU2lnID09IDEpKSksIDEpKQoKI3Bsb3R0aW5nIE5OIGhvdHNwb3RzCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gZmluYWxfbmV0LCBhZXMoZmlsbD1wZXJtaXQuaXNTaWcuZGlzdCksIGNvbG91ciA9IE5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWU9Ik5OIERpc3RhbmNlIikgKwogICAgICBsYWJzKHRpdGxlPSJQZXJtaXQgTk4gRGlzdGFuY2UiLCBjYXB0aW9uPSAiRmlndXJlIHh4IikgKwogICAgICBtYXBUaGVtZSgpKwogICAgICBwbG90VGhlbWUoKQoKI2xtb3JhbiA8LSBsb2NhbG1vcmFuKGZpbmFsX25ldCRsaWdodHNvdXQsIGZpbmFsX25ldC53ZWlnaHRzLCAgemVyby5wb2xpY3k9VFJVRSkKCiNmaW5hbF9uZXQkbG1JIDwtIGxtb3JhblssICJJaSJdICMgbG9jYWwgTW9yYW4ncyBJCiNmaW5hbF9uZXQkbG1aIDwtIGxtb3JhblssICJaLklpIl0gIyB6LXNjb3JlcwojZmluYWxfbmV0JGxtcCA8LSBsbW9yYW5bLCAiUHIoeiAhPSBFKElpKSkiXQoKCiNtcCA8LSBtb3Jhbi5wbG90KGFzLnZlY3RvcihzY2FsZShmaW5hbF9uZXQkbGlnaHRzb3V0KSksIGZpbmFsX25ldC53ZWlnaHRzLCB6ZXJvLnBvbGljeSA9IFRSVUUpCgojI0NyZWF0ZSBhIGhvdHNwb3QgdmFyaWFibGU6CiNmaW5hbF9uZXQkbG1wIDwtIGlmZWxzZShpcy5uYW4oZmluYWxfbmV0JGxtcCksIDAuMTAsIGZpbmFsX25ldCRsbXApCiNmaW5hbF9uZXQkaG90c3BvdCA8LSAwCiNmaW5hbF9uZXRbKG1wJHggPj0wICYgbXAkd3ggPj0wKSAmIGZpbmFsX25ldCRsbXAgPD0gMC4wNSwgImhvdHNwb3QiXTwtIDEKYGBgCgpgYGB7ciBob3RzcG90LCByZXN1bHRzID0gJ2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBnZW5lcmF0ZXMgd2FybmluZyBmcm9tIE5OCiNmaW5hbF9uZXQgPC0gZmluYWxfbmV0ICU+JSAKICAjbXV0YXRlKGxpZ2h0c291dC5pc1NpZy5kaXN0ID0gCiAgICAgICAgICAgI25uX2Z1bmN0aW9uKHN0X2Moc3RfY29pZChmaW5hbF9uZXQpKSwKICAgICAgICAgICAgICAgICAgICAgICAjc3RfYyhzdF9jb2lkKGZpbHRlcihmaW5hbF9uZXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgIGhvdHNwb3QgPT0gMSkpKSwgCiMgICAgICAgICAgICAgICAgICAgICAgIGsgPSAxKSkKCmBgYAoKCmBgYHtyIHBsb3R0eSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiNnZ3Bsb3QoKSArCiMgICAgICBnZW9tX3NmKGRhdGEgPSBmaW5hbF9uZXQsIGFlcyhmaWxsPWxpZ2h0c291dC5pc1NpZy5kaXN0KSwgY29sb3VyPU5BKSArCiMgICAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZT0iTk4gRGlzdGFuY2UiKSArCiMgICAgICBsYWJzKHRpdGxlPSJBbGxleSBMaWdodHMgT3V0IE5OIERpc3RhbmNlIikgKwojICAgICAgdGhlbWVfdm9pZCgpCmBgYAoKIyMgQnVpbGRpbmcgYW5kIGV2YWx1YXRpbmcgdGhlIG1vZGVsCgpUaGUgbW9kZWwgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGlzIGEgcG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsLiBUaGUgY29sbGVjdGVkIGFuZCBjbGVhbmVkIGRhdGEgdGhhdCB3YXMgdXNlZCBpbiB0aGlzIG1vZGVsIHdhcyBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiBsaWtlIHJhY2UsIGluY29tZSwgYW5kIHdvcmsgc3RhdHVzOyBob3VzaW5nIGluZm9ybWF0aW9uIGxpa2UgcGVyY2VudCByZW50ZXIgYW5kIG93bmVyIG9jY3VwaWVkOyByZWdpb25hbCBjaGFyYWN0ZXJpc3RpY3MgbGlrZSB2YWNhbnQgYnVpbGRpbmdzLCBjb25zdHJ1Y3Rpb24gcGVybWl0cywgYWZmb3JkYWJsZSBob3VzaW5nLCBhbmQgZGVtb2xpdGlvbnMuCgojIyMgQ29ycmVsYXRpb24KCmBgYHtyIC0gY29ycmVsYXRpb24gdGVzdHMgZm9yIHNwYXRpYWwgYW5kIGRlbW9ncmFwaGljIGZhY3RvcnMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKY29ycmVsYXRpb24ubG9uZyA8LQogIHN0X2Ryb3BfZ2VvbWV0cnkoZmluYWxfbmV0KSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLXVuaXF1ZUlELCAtY3ZJRCwgLW5hbWUpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWNvdW50UGVybWl0cykgJT4lCiAgbXV0YXRlKFZhbHVlID0gYXMubnVtZXJpYyhWYWx1ZSkpCgpjb3JyZWxhdGlvbi5jb3IgPC0KICBjb3JyZWxhdGlvbi5sb25nICU+JQogICAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JQogICAgc3VtbWFyaXplKGNvcnJlbGF0aW9uID0gY29yKFZhbHVlLCBjb3VudFBlcm1pdHMsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSkKCiMgVmlzdWFsaXppbmcgY29ycmVsYXRpb25zIHRocm91Z2ggc2NhdHRlciBwbG90cwojd2UgaGF2ZSBhIGxvdCBvZiBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMgaGVyZSB0aGF0IGkgZG9uJ3Qga25vdyBpZiB3ZSBuZWNlc3NhcmlseSBuZWVkIG9yIGFyZSBpbnRlcmVzdGVkIGluIGtlZXBpbmcgZm9yIG91ciBmaW5hbCBzdHVmZiAtIHRoaW5rIGl0IG1heSBiZSBiZXR0ZXIgdG8gcGljayBhbmQgY2hvb3NlIGZld2VyIGRlbW8gdmFyaWFibGVzIGFuZCBtYXliZSBjaG9vc2UgbW9yZSBleHRlcm5hbCB2YXJpYWJsZXMKICAgIApnZ3Bsb3QoY29ycmVsYXRpb24ubG9uZywgYWVzKFZhbHVlLCBjb3VudFBlcm1pdHMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC4xKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBjb3JyZWxhdGlvbi5jb3IsIGFlcyhsYWJlbCA9IHBhc3RlKCJyID0iLCByb3VuZChjb3JyZWxhdGlvbiwgMikpKSwKICAgICAgICAgICAgeD0tSW5mLCB5PUluZiwgdmp1c3QgPSAxLjUsIGhqdXN0ID0gLS4xKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3VyID0gIm9yYW5nZSIpICsKICBmYWNldF93cmFwKH5WYXJpYWJsZSwgbmNvbCA9IDQsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGUgPSAiUGVybWl0IENvdW50IGFzIGEgZnVuY3Rpb24gb2YgcmlzayBmYWN0b3JzIiwgY2FwdGlvbj0iRmlndXJlIDEyIikgKwogIHBsb3RUaGVtZSgpCgpgYGAKCgpgYGB7ciAtIGNvcnJlbGF0aW9uIG1hdHJpeCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CgpudW12YXJzIDwtIGMoImNvdW50UGVybWl0cyIsICJkaXN0X3ZhY2FudEJ1aWxkaW5nIiwgICJkaXN0X2FmZm9yZGFibGVIb3VzaW5nIiwiZGlzdF9ncmVlblNwYWNlIiwgImRpc3RfYnVpbGRpbmdEZW1vbGl0aW9uIiwgInRvdGFsUG9wIiwgIm1lZEhISW5jIiwgInBjdFdoaXRlIiwgInBjdEJsYWNrIiwgInBjdEJhY2giICwicGN0UG92IiwgInBjdFVuZW1wbG95IiwgInBjdE93bmVyT2NjdXBpZWQiLCAicGN0UmVudGVyT2NjdXBpZWQiKQoKbnVtZXJpYyA8LSBmaW5hbF9uZXQgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeShmaW5hbF9uZXQpICU+JQogIGRwbHlyOjpzZWxlY3QobnVtdmFycyklPiUKICBuYS5vbWl0KCkKCmdnY29ycnBsb3QoCiAgcm91bmQoY29yKG51bWVyaWMpLCAxKSwgCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljKSwKICBjb2xvcnMgPSBjKCcjZDcxOTFjJywnd2hpdGUnLCcjMmM3YmI2JyksCiAgdHlwZT0ibG93ZXIiLAogIGluc2lnID0gImJsYW5rIikgKyAgCiAgICBsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9uIGFjcm9zcyBWYXJpYWJsZXNcbiIsIGNhcHRpb249IkZpZ3VyZSAxMyIpKyAKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJkYXJrcmVkIikpKwogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCkpKwogICAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCkpCgpgYGAKVGhlcmUgc2VlbXMgdG8gYmUgcmVsYXRpdmVseSBoaWdoIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gbWVkaWFuIGhvdXNlaG9sZCBpbmNvbWUgYW5kIHRoZSBwZXJjZW50IG9mIHRoZSBwb3B1bGF0aW9uIHRoYXQgaXMgd2hpdGUuIFRoaXMgbWF5IHN1Z2dlc3QgdGhhdCBhcmVhcyB3aXRoIG1vcmUgd2hpdGUgcmVzaWRlbnRzIGFyZSBoaWdoZXIgZWFybmluZyBuZWlnaGJvcmhvb2RzLCBvciBhIG5laWdoYm9yaG9vZCB0aGF0IGlzIGdlbnRyaWZ5aW5nIG1heSBoYXZlIG1vcmUgaGlnaGVyIGluY29tZSBhbmQgbW9yZSB3aGl0ZSByZXNpZGVudHMuIFRoZXJlIGlzIGEgcmVsYXRpdmVseSBoaWdoIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdG90YWwgcG9wdWxhdGlvbiBhbmQgdGhlIGFtb3VudCBvZiB2YWNhbnQgYnVpbGRpbmdzLiBUaGlzIG1heSBtZWFuIHRoYXQgdGhlcmUgYXJlIGxlc3MgdmFjYW50IGJ1aWxkaW5ncyBpbiBtb3JlIHBvcHVsb3VzIGFyZWFzIC0gdGhpcyBjb3VsZCByZWxhdGUgdG8gZ2VudHJpZmljYXRpb24gaW4gdGhlIHdheSB0aGF0IG1vcmUgY3Jvd2RlZCwgY292ZXRlZCBuZWlnaGJvcmhvb2RzIHdpbGwgYmUgbW9yZSBwcmVzc2VkIGZvciBzcGFjZSwgbGVhZGluZyB0byByZWRldmVsb3BtZW50IG9mIHZhY2FudCBwcm9wZXJ0aWVzLgoKQ29uZHVjdGluZyBhIHJlZ3Jlc3Npb24gbW9kZWwgaW4gdGhpcyBjb250ZXh0IGlzIGNydWNpYWwgZm9yIHNldmVyYWwgcmVhc29ucy4gRmlyc3RseSwgaXQgYWxsb3dzIGZvciB0aGUgcXVhbnRpZmljYXRpb24gb2YgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlvdXMgZmFjdG9ycyBhbmQgdGhlIG91dGNvbWUgb2YgaW50ZXJlc3QsIHN1Y2ggYXMgdGhlIG51bWJlciBvZiBwZXJtaXRzIGlzc3VlZCBvciB0aGUgbGlrZWxpaG9vZCBvZiBkZXZlbG9wbWVudC4gVGhpcyBlbmFibGVzIHRoZSBpZGVudGlmaWNhdGlvbiBvZiBzaWduaWZpY2FudCBwcmVkaWN0b3JzIGFuZCB0aGVpciByZXNwZWN0aXZlIGltcGFjdCBvbiB0aGUgb3V0Y29tZSwgYWlkaW5nIGluIHVuZGVyc3RhbmRpbmcgdGhlIHVuZGVybHlpbmcgZHluYW1pY3Mgb2YgZGV2ZWxvcG1lbnQgcmlzay4gQWRkaXRpb25hbGx5LCByZWdyZXNzaW9uIG1vZGVsaW5nIHByb3ZpZGVzIGEgbWVhbnMgdG8gYXNzZXNzIHRoZSByZWxhdGl2ZSBpbXBvcnRhbmNlIG9mIGRpZmZlcmVudCB2YXJpYWJsZXMsIHByaW9yaXRpemUgaW50ZXJ2ZW50aW9ucywgYW5kIGluZm9ybSBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzc2VzIGFpbWVkIGF0IG1pdGlnYXRpbmcgZGV2ZWxvcG1lbnQtcmVsYXRlZCBjaGFsbGVuZ2VzIG9yIHByb21vdGluZyBzdXN0YWluYWJsZSB1cmJhbiBncm93dGguCgpDcm9zcy12YWxpZGF0aW9uIGlzIHBlcmZvcm1lZCB0byBldmFsdWF0ZSB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvZiBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWxzIGFwcGxpZWQgdG8gdGhlIGRhdGFzZXQuIFRoaXMgcHJvY2VzcyBpbnZvbHZlcyBzcGxpdHRpbmcgdGhlIGRhdGFzZXQgaW50byBkaXN0aW5jdCBmb2xkcywgd2hlcmUgZWFjaCBmb2xkIHNlcnZlcyBhcyBhIHRyYWluaW5nIHNldCBmb3IgbW9kZWwgZGV2ZWxvcG1lbnQgYW5kIGEgdGVzdGluZyBzZXQgZm9yIHBlcmZvcm1hbmNlIGFzc2Vzc21lbnQuIFdpdGhpbiBlYWNoIGl0ZXJhdGlvbiwgYSBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwgaXMgdHJhaW5lZCB1c2luZyB0aGUgdHJhaW5pbmcgZGF0YSBhbmQgdGhlbiBhcHBsaWVkIHRvIHRoZSB0ZXN0aW5nIGRhdGEgdG8gbWFrZSBwcmVkaWN0aW9ucy4gVGhlc2UgcHJlZGljdGlvbnMgYXJlIGNvbXBhcmVkIGFnYWluc3QgdGhlIGFjdHVhbCB2YWx1ZXMgdG8gYXNzZXNzIHRoZSBtb2RlbCdzIGFjY3VyYWN5IGFuZCByb2J1c3RuZXNzLiBUaGUgY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZWR1cmUgaGVscHMgZW5zdXJlIHRoYXQgdGhlIG1vZGVscyBnZW5lcmFsaXplIHdlbGwgdG8gdW5zZWVuIGRhdGEgYW5kIHByb3ZpZGUgcmVsaWFibGUgcHJlZGljdGlvbnMgaW4gcmVhbC13b3JsZCBzY2VuYXJpb3MsIGVuaGFuY2luZyB0aGUgb3ZlcmFsbCByZWxpYWJpbGl0eSBvZiB0aGUgYW5hbHlzaXMuCgpUaGUgcHJvdmlkZWQgY29kZSBpbXBsZW1lbnRzIGEgY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZWR1cmUgZm9yIGV2YWx1YXRpbmcgUG9pc3NvbiByZWdyZXNzaW9uIG1vZGVscyBhcHBsaWVkIHRvIHRoZSBkYXRhc2V0IGBmaW5hbF9uZXRgLiBJdCBkZWZpbmVzIGEgZnVuY3Rpb24gYGNyb3NzVmFsaWRhdGUoKWAgdG8gY29uZHVjdCBjcm9zcy12YWxpZGF0aW9uLCB3aGVyZSB0aGUgZGF0YXNldCBpcyBzcGxpdCBpbnRvIGRpc3RpbmN0IGZvbGRzIChgY3ZJRGApIGZvciB0cmFpbmluZyBhbmQgdGVzdGluZy4gV2l0aGluIGVhY2ggZm9sZCwgYSBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwgaXMgdHJhaW5lZCB1c2luZyB0aGUgdHJhaW5pbmcgZGF0YSAoYGZvbGQudHJhaW5gKSBhbmQgdGhlbiBhcHBsaWVkIHRvIHRoZSB0ZXN0aW5nIGRhdGEgKGBmb2xkLnRlc3RgKSB0byBtYWtlIHByZWRpY3Rpb25zLiBUaGVzZSBwcmVkaWN0aW9ucyBhcmUgYWdncmVnYXRlZCBhY3Jvc3MgYWxsIGZvbGRzIHRvIGFzc2VzcyB0aGUgb3ZlcmFsbCBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbC4gVGhlIGNyb3NzLXZhbGlkYXRpb24gaGVscHMgdmFsaWRhdGUgdGhlIG1vZGVsJ3MgYWJpbGl0eSB0byBnZW5lcmFsaXplIHRvIHVuc2VlbiBkYXRhLCBlbnN1cmluZyB0aGUgcmVsaWFiaWxpdHkgb2YgdGhlIHJlZ3Jlc3Npb24gYW5hbHlzaXMuCgojIyMgVmFyaWFibGUgU2VsZWN0aW9uCgpCYXNlZCBvbiB0aGUgY29ycmVsYXRpb24gYW5hbHlzaXMsIHZhcmlhYmxlcyBzdWNoIGFzICdWYWNhbnQgQnVpbGRpbmcgRGlzdGFuY2UnLCAnQWZmb3JkYWJsZSBIb3VzaW5nIERpc3RhbmNlJywgJ0dyZWVuIFNwYWNlIERpc3RhbmNlJywgJ0J1aWxkaW5nIERlbW9saXRpb24gRGlzdGFuY2UnLCAnTWVkaWFuIEhvdXNlaG9sZCBJbmNvbWUnLCAnUGVyY2VudCBCZWxvdyBQb3ZlcnR5IExpbmUnLCAnUGVyY2VudCBVbmVtcGxveWVkJywgYW5kICdQZXJjZW50IE93bmVyIE9jY3VwaWVkJyBhcmUgc2VsZWN0ZWQgYXMgbW9kZWwgdmFyaWFibGVzLiAKCmBgYHtyIHJlZ3Jlc3Npb24sIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KCiMjIGRlZmluZSB0aGUgdmFyaWFibGVzIHdlIHdhbnQKcmVnLnZhcnMgPC0gYygiZGlzdF92YWNhbnRCdWlsZGluZyIsICAiZGlzdF9hZmZvcmRhYmxlSG91c2luZyIsImRpc3RfZ3JlZW5TcGFjZSIsICJkaXN0X2J1aWxkaW5nRGVtb2xpdGlvbiIsICJtZWRISEluYyIsICJwY3RCYWNoIiAsInBjdFBvdiIsICJwY3RVbmVtcGxveSIsICJwY3RPd25lck9jY3VwaWVkIikKCnJlZy5zcy52YXJzIDwtIGMoImRpc3RfdmFjYW50QnVpbGRpbmciLCAgImRpc3RfYWZmb3JkYWJsZUhvdXNpbmciLCJkaXN0X2dyZWVuU3BhY2UiLCAiZGlzdF9idWlsZGluZ0RlbW9saXRpb24iLCAibWVkSEhJbmMiLCAicGN0QmFjaCIgLCJwY3RQb3YiLCAicGN0VW5lbXBsb3kiLCAicGN0T3duZXJPY2N1cGllZCIsICJwZXJtaXQuaXNTaWciKQoKIyMgY3JlYXRpbmcgZnVuY3Rpb25zIGZvciBjcm9zcyB2YWxpZGF0aW9uCgpjcm9zc1ZhbGlkYXRlIDwtIGZ1bmN0aW9uKGRhdGFzZXQsIGlkLCBkZXBlbmRlbnRWYXJpYWJsZSwgaW5kVmFyaWFibGVzKSB7CiAgCiAgYWxsUHJlZGljdGlvbnMgPC0gZGF0YS5mcmFtZSgpCiAgY3ZJRF9saXN0IDwtIHVuaXF1ZShkYXRhc2V0W1tpZF1dKQogIAogIGZvciAoaSBpbiBjdklEX2xpc3QpIHsKICAgIAogICAgdGhpc0ZvbGQgPC0gaQogICAgY2F0KCJUaGlzIGhvbGQgb3V0IGZvbGQgaXMiLCB0aGlzRm9sZCwgIlxuIikKICAgIAogICAgZm9sZC50cmFpbiA8LSBmaWx0ZXIoZGF0YXNldCwgZGF0YXNldFtbaWRdXSAhPSB0aGlzRm9sZCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoaWQsIGdlb21ldHJ5LCBpbmRWYXJpYWJsZXMsIGRlcGVuZGVudFZhcmlhYmxlKQogICAgZm9sZC50ZXN0ICA8LSBmaWx0ZXIoZGF0YXNldCwgZGF0YXNldFtbaWRdXSA9PSB0aGlzRm9sZCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoaWQsIGdlb21ldHJ5LCBpbmRWYXJpYWJsZXMsIGRlcGVuZGVudFZhcmlhYmxlKQogICAgCiAgICByZWdyZXNzaW9uIDwtCiAgICAgIGdsbShjb3VudFBlcm1pdHMgfiAuLCBmYW1pbHkgPSAicG9pc3NvbiIsIAogICAgICAgICAgZGF0YSA9IGZvbGQudHJhaW4gJT4lIAogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1nZW9tZXRyeSwgLWlkKSkKICAgIAogICAgdGhpc1ByZWRpY3Rpb24gPC0gCiAgICAgIG11dGF0ZShmb2xkLnRlc3QsIFByZWRpY3Rpb24gPSBwcmVkaWN0KHJlZ3Jlc3Npb24sIGZvbGQudGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpKQogICAgCiAgICBhbGxQcmVkaWN0aW9ucyA8LQogICAgICByYmluZChhbGxQcmVkaWN0aW9ucywgdGhpc1ByZWRpY3Rpb24pCiAgICAKICB9CiAgcmV0dXJuKHN0X3NmKGFsbFByZWRpY3Rpb25zKSkKfQpgYGAKCiMjIyBDcm9zcyBWYWxpZGF0aW9uCgpUaGlzIHNob3dzIHRoZSBwcm9jZXNzIG9mIGNvbmR1Y3RpbmcgY3Jvc3MtdmFsaWRhdGlvbiBvbiBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWxzIHVzaW5nIHRoZSBjcm9zc1ZhbGlkYXRlIGZ1bmN0aW9uLCBhaW1lZCBhdCBwcmVkaWN0aW5nIHRoZSBudW1iZXIgb2YgY29uc3RydWN0aW9uIHBlcm1pdHMgKGNvdW50UGVybWl0cykgaW4gUGhpbGFkZWxwaGlhIG5laWdoYm9yaG9vZHMuIERpZmZlcmVudCBtb2RlbHMgYXJlIGJlaW5nIGV2YWx1YXRlZDogYSBub24tc3BhdGlhbCBwcm9jZXNzIG1vZGVsIHVzaW5nIGEgZGVmaW5lZCBzZXQgb2YgdmFyaWFibGVzIChyZWcudmFycykgYW5kIGEgc3BhdGlhbCBwcm9jZXNzIG1vZGVsLCB3aGljaCBpbnRlZ3JhdGVzIHNwYXRpYWwgY2hhcmFjdGVyaXN0aWNzIGFsb25nIHdpdGggdGhlIHNhbWUgc2V0IG9mIHZhcmlhYmxlcyB0byB1bmRlcnN0YW5kIGhvdyBpbmNsdWRpbmcgc3BhdGlhbCBkYXRhIGluZmx1ZW5jZXMgdGhlIGFjY3VyYWN5IGFuZCBwcmVkaWN0aXZlbmVzcyBvZiB0aGUgbW9kZWwgb3V0Y29tZXMuIFRoaXMgY3Jvc3MtdmFsaWRhdGlvbiBhcHByb2FjaCBoZWxwcyBpbiBhc3Nlc3NpbmcgdGhlIG1vZGVsJ3Mgcm9idXN0bmVzcyBhbmQgZ2VuZXJhbGl6YWJpbGl0eSBieSB0ZXN0aW5nIGl0IG9uIG11bHRpcGxlIHN1YnNldHMgb2YgZGF0YS4KCmBgYHtyIGNyb3NzIHZhbGlkYXRpb24sIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KCiNjb25kdWN0aW5nIGNyb3NzIHZhbGlkYXRpb24gb24gUG9pc3NvbiByZWdyZXNzaW5vIG1vZGVscwoKcmVnLmN2IDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGZpbmFsX25ldCwKICBpZCA9ICJjdklEIiwKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJjb3VudFBlcm1pdHMiLAogIGluZFZhcmlhYmxlcyA9IHJlZy52YXJzKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoY3ZJRCA9IGN2SUQsIGNvdW50UGVybWl0cywgUHJlZGljdGlvbiwgZ2VvbWV0cnkpCgpyZWcuc3MuY3YgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gZmluYWxfbmV0LAogIGlkID0gImN2SUQiLAogIGRlcGVuZGVudFZhcmlhYmxlID0gImNvdW50UGVybWl0cyIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnNzLnZhcnMpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gY3ZJRCwgY291bnRQZXJtaXRzLCBQcmVkaWN0aW9uLCBnZW9tZXRyeSkKCmZpbmFsX25ldCRuYW1lIDwtIGlmZWxzZShpcy5uYShmaW5hbF9uZXQkbmFtZSksICJVTktOT1dOIiwgZmluYWxfbmV0JG5hbWUpCiAgCnJlZy5zcGF0aWFsQ1YgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gZmluYWxfbmV0LAogIGlkID0gIm5hbWUiLAogIGRlcGVuZGVudFZhcmlhYmxlID0gImNvdW50UGVybWl0cyIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnZhcnMpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gbmFtZSwgY291bnRQZXJtaXRzLCBQcmVkaWN0aW9uLCBnZW9tZXRyeSkKCnJlZy5zcy5zcGF0aWFsQ1YgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gZmluYWxfbmV0LAogIGlkID0gIm5hbWUiLAogIGRlcGVuZGVudFZhcmlhYmxlID0gImNvdW50UGVybWl0cyIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnNzLnZhcnMpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gbmFtZSwgY291bnRQZXJtaXRzLCBQcmVkaWN0aW9uLCBnZW9tZXRyeSkKYGBgCgojIyMgRXJyb3IgQ2FsY3VsYXRpb24KCkluIGVhY2ggcmVncmVzc2lvbiwgdGhlIGFic29sdXRlIGVycm9yIGlzIGRldGVybWluZWQgYnkgY2FsY3VsYXRpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJlZGljdGVkIGFuZCB0aGUgYWN0dWFsIG9ic2VydmVkIGNvdW50cyBvZiBuZXcgY29uc3RydWN0aW9uIHBlcm1pdHMuCgpgYGB7ciByZWdyZXNzaW9uIGVycm9ycywgcmVzdWx0cz0naGlkZScsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIGNhbGN1bGF0ZSBlcnJvcnMKCnJlZy5zdW1tYXJ5IDwtIAogIHJiaW5kKAogICAgbXV0YXRlKHJlZy5jdiwgICAgICAgICAgIEVycm9yID0gUHJlZGljdGlvbiAtIGNvdW50UGVybWl0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlJhbmRvbSBrLWZvbGQgQ1Y6IEp1c3QgUmlzayBGYWN0b3JzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICBtdXRhdGUocmVnLnNzLmN2LCAgICAgICAgRXJyb3IgPSBQcmVkaWN0aW9uIC0gY291bnRQZXJtaXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiUmFuZG9tIGstZm9sZCBDVjogU3BhdGlhbCBQcm9jZXNzIiksCiAgICAKICAgIG11dGF0ZShyZWcuc3BhdGlhbENWLCAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBjb3VudFBlcm1pdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVncmVzc2lvbiA9ICJTcGF0aWFsIExPR08tQ1Y6IEp1c3QgUmlzayBGYWN0b3JzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICBtdXRhdGUocmVnLnNzLnNwYXRpYWxDViwgRXJyb3IgPSBQcmVkaWN0aW9uIC0gY291bnRQZXJtaXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiU3BhdGlhbCBMT0dPLUNWOiBTcGF0aWFsIFByb2Nlc3MiKSkgJT4lCiAgICBzdF9zZigpCmBgYAoKCmBgYCB7ciBlcnJvcnMsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KCiMgQ2FsY3VsYXRlIE1BRSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBlYWNoIGZvbGQgYW5kIG1ldGhvZAplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgPC0gCiAgcmVnLnN1bW1hcnkgJT4lIAogIGdyb3VwX2J5KFJlZ3Jlc3Npb24sIGN2SUQpICU+JSAKICBzdW1tYXJpemUoTWVhbl9FcnJvciA9IG1lYW4oUHJlZGljdGlvbiAtIGNvdW50UGVybWl0cywgbmEucm0gPSBUKSwKICAgIE1BRSA9IG1lYW4oYWJzKE1lYW5fRXJyb3IpLCBuYS5ybSA9IFRSVUUpLAogICAgU0RfTUFFID0gbWVhbihhYnMoTWVhbl9FcnJvciksIG5hLnJtID0gVFJVRSksCiAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgKQoKIyBBcnJhbmdlIGJ5IE1BRSBmb3Igdmlld2luZwplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgJT4lIAogIGFycmFuZ2UoZGVzYyhNQUUpKQplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgJT4lIAogIGFycmFuZ2UoTUFFKQoKIyBQbG90IGhpc3RvZ3JhbSBvZiBPT0YgZXJyb3JzIGZvciBlYWNoIG1ldGhvZAplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgJT4lCiAgZ2dwbG90KGFlcyh4ID0gTUFFKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG91cj0iYmxhY2siLCBmaWxsID0gIiNGREU3MjVGRiIpICsKICBmYWNldF93cmFwKH4gUmVncmVzc2lvbiwgc2NhbGVzID0gImZyZWUiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMSwgYnkgPSAxKSkgKwogIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBNQUUiLCBzdWJ0aXRsZSA9ICJSYW5kb20gSy1Gb2xkIGFuZCBMT0dPLUNWIiwKICAgICAgIHg9Ik1lYW4gQWJzb2x1dGUgRXJyb3IiLCB5PSJDb3VudCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgZmFjZT0gImJvbGQiLCBjb2xvciA9ICJkYXJrcmVkIikpCmBgYAoKCgpgYGAge3Igc3BhdGlhbCBsb2dvIGN2IGVycm9ycywgcmVzdWx0cz0naGlkZScsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QoUmVncmVzc2lvbiwgIkxPR08iKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHBobEJvdW5kYXJ5LCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiZGFya2dyZXkiKSArCiAgICBnZW9tX3NmKGFlcyhmaWxsID0gTUFFKSkgKwogICAgZmFjZXRfd3JhcCh+UmVncmVzc2lvbikgKwogICAgc2NhbGVfY29sb3VyX3ZpcmlkaXMob3B0aW9uID0gInBsYXNtYSIpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAicGxhc21hIikgKwogICAgbGFicyh0aXRsZSA9ICJFcnJvcnMgYnkgTE9HTy1DViBSZWdyZXNzaW9uIiwgY2FwdGlvbj0iRmlndXJlIDE1IikgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsIGZhY2U9ICJib2xkIiwgY29sb3IgPSAiZGFya3JlZCIpKSsKICAgIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQoKYGBgCgoKYGBge3IgdGFibGVzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBUYWJsZSBvZiBNQUUgYW5kIFN0YW5kYXJkIERldmlhdGlvbiBNQUUKCnN0X2Ryb3BfZ2VvbWV0cnkoZXJyb3JfYnlfcmVnX2FuZF9mb2xkKSAlPiUKICBncm91cF9ieShSZWdyZXNzaW9uKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9NQUUgPSByb3VuZChtZWFuKE1BRSksIDIpLAogICAgICAgICAgICAgIFNEX01BRSA9IHJvdW5kKHNkKE1BRSksIDIpKSAlPiUKICBrYWJsZShjYXB0aW9uID0gIlRhYmxlIDE6IE1BRSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIE1BRSBieSByZWdyZXNzaW9uIikgJT4lCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYpICU+JQogICAgcm93X3NwZWMoMiwgY29sb3IgPSAiYmxhY2siLCBiYWNrZ3JvdW5kID0gIiNGREU3MjVGRiIpICU+JQogICAgcm93X3NwZWMoNCwgY29sb3IgPSAiYmxhY2siLCBiYWNrZ3JvdW5kID0gIiNGREU3MjVGRiIpIAoKYGBgClRoZSBhcmVhcyB3aXRoIHNvbWUgb2YgdGhlIGdyZWF0ZXN0IG1lYW4gYWJzb2x1dGUgZXJyb3IgYXJlIGNsb3NlciB0byBjZW50ZXIgY2l0eSwgc3VjaCBhcyBGaXNodG93biwgVW5pdmVyc2l0eSBDaXR5LCBhbmQgR3JhZHVhdGUgSG9zcGl0YWwuIFJlZ2lvbnMgb2YgUGhpbGFkZWxwaGlhIGNsb3NlIHRvIHRoZSBOb3J0aCwgV2VzdCwgU291dGgsIGFuZCBhbG9uZyB0aGUgRGVsYXdhcmUgcml2ZXIgc2hvd2VkIGxvd2VyIGVycm9ycywgYm90aCBpbiB0ZXJtcyBvZiBqdXN0IHJpc2sgZmFjdG9ycyBhbmQgc3BhdGlhbCBwcm9jZXNzLiBUaGVyZSB3YXMgaGlnaGVyIGVycm9yIHNob3duIGluIHRoZSBTcGF0aWFsIExPR08tQ1YgcHJvY2VzcyB0aGFuIHRoZSBvdGhlciB2YWxpZGF0aW9uIG1ldGhvZHMuIFRoZSByYW5kb20gay1mb2xkIHByb2Nlc3NlcyByZXN1bHRlZCBpbiBsb3dlciBtZWFuIGFic29sdXRlIGVycm9yLgoKYGBgIHtyIG1vcmFucyBpIGVycm9ycywgcmVzdWx0cz0naGlkZScsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIEFzc3VtaW5nICdlcnJvcl9ieV9yZWdfYW5kX2ZvbGQnIGNvbnRhaW5zIHRoZSBuZWNlc3NhcnkgY29sdW1ucyBhbmQgc3BhdGlhbCBkYXRhCiMgRmlyc3QsIGVuc3VyZSB0aGF0IHlvdXIgZGF0YSBmcmFtZSBoYXMgdGhlIGFwcHJvcHJpYXRlIHN0cnVjdHVyZSBhbmQgdW5pcXVlIHJvdyBuYW1lcwoKIyBDaGVjayBmb3IgdW5pcXVlIHJvdyBuYW1lcyBhbmQgcmVzZXQgaWYgbmVjZXNzYXJ5CmlmKGFueUR1cGxpY2F0ZWQocm93Lm5hbWVzKGVycm9yX2J5X3JlZ19hbmRfZm9sZCkpKSB7CiAgcm93bmFtZXMoZXJyb3JfYnlfcmVnX2FuZF9mb2xkKSA8LSBtYWtlLnVuaXF1ZShhcy5jaGFyYWN0ZXIocm93Lm5hbWVzKGVycm9yX2J5X3JlZ19hbmRfZm9sZCkpKQp9CgojIENyZWF0ZSB3ZWlnaHRzIG9ubHkgZm9yIHRoZSBzZWxlY3RlZCByZWdyZXNzaW9uIHR5cGUKbmVpZ2hib3Job29kLndlaWdodHMgPC0gZXJyb3JfYnlfcmVnX2FuZF9mb2xkICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJTcGF0aWFsIExPR08tQ1Y6IFNwYXRpYWwgUHJvY2VzcyIpICU+JQogIHN0X2FzX3NmKCkgJT4lCiAgZ3JvdXBfYnkoY3ZJRCkgJT4lCiAgcG9seTJuYiguLCBxdWVlbiA9IFRSVUUpICU+JSAgIyBDb3JyZWN0ZWQ6IFJlbW92ZWQgc3R5bGUgYW5kIHplcm8ucG9saWN5CiAgbmIybGlzdHcoLiwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkgICMgQ29ycmVjdCB1c2FnZSBvZiBzdHlsZSBhbmQgemVyby5wb2xpY3kKCmZpbHRlcihlcnJvcl9ieV9yZWdfYW5kX2ZvbGQsIHN0cl9kZXRlY3QoUmVncmVzc2lvbiwgIkxPR08iKSkgICU+JSAKICAgIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICAgIGdyb3VwX2J5KFJlZ3Jlc3Npb24pICU+JQogICAgc3VtbWFyaXplKE1vcmFuc19JID0gbW9yYW4ubWMoYWJzKE1lYW5fRXJyb3IpLCBuZWlnaGJvcmhvb2Qud2VpZ2h0cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zaW0gPSA5OTksIHplcm8ucG9saWN5ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLmFjdGlvbj1uYS5vbWl0KVtbMV1dLAogICAgICAgICAgICAgIHBfdmFsdWUgPSBtb3Jhbi5tYyhhYnMoTWVhbl9FcnJvciksIG5laWdoYm9yaG9vZC53ZWlnaHRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNpbSA9IDk5OSwgemVyby5wb2xpY3kgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEuYWN0aW9uPW5hLm9taXQpW1szXV0pICU+JSAKICBrYWJsZShjYXB0aW9uID0gIlRhYmxlIDI6IE1vcmFuJ3MgSSBvbiBFcnJvcnMgYnkgUmVncmVzc2lvbiIpICU+JQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUKICAgIHJvd19zcGVjKDEsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjRkRFNzI1RkYiKSAlPiUKICAgIHJvd19zcGVjKDEsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjRkRFNzI1RkYiKSAKCgpgYGAKVGhpcyBkaXNwbGF5cyB0aGUgcmVzaWR1YWxzIG9mIGRpZmZlcmVudCByZWdyZXNzaW9uIG1vZGVscyB1c2VkIHRvIHByZWRpY3QgbmV3IGNvbnN0cnVjdGlvbiBwZXJtaXRzLCBzZWdtZW50ZWQgYnkgbW9kZWwgdHlwZS4gRnJvbSB0aGUgcGxvdCwgaXQncyBldmlkZW50IHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiByZXNpZHVhbHMgdmFyaWVzIGFjcm9zcyB0aGUgbW9kZWxzLCB3aXRoIHNvbWUgc2hvd2luZyBhIHRpZ2h0ZXIgY2x1c3RlciBhcm91bmQgdGhlIHplcm8gbGluZSAoaW5kaWNhdGluZyBiZXR0ZXIgcHJlZGljdGlvbiBhY2N1cmFjeSkgYW5kIG90aGVycyBkaXNwbGF5aW5nIG1vcmUgc3ByZWFkLCBzdWdnZXN0aW5nIGxlc3MgcHJlY2lzaW9uLiAKCmBgYHtyIHJlc2lkdWFscywgcmVzdWx0cz0naGlkZScsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKcmVnLnN1bW1hcnkgPC0gcmVnLnN1bW1hcnkgJT4lCiAgbXV0YXRlKFJlc2lkdWFscyA9IGNvdW50UGVybWl0cyAtIFByZWRpY3Rpb24pCgojIHJlc2lkdWFsIHBsb3QKCmdncGxvdChyZWcuc3VtbWFyeSwgYWVzKHggPSBQcmVkaWN0aW9uLCB5ID0gUmVzaWR1YWxzKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjQpICsgICMgVXNlIGFscGhhIHRvIGFkanVzdCBwb2ludCB0cmFuc3BhcmVuY3kKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArICAjIEFkZCBhIGhvcml6b250YWwgbGluZSBhdCB5ID0gMAogIGxhYnModGl0bGUgPSAiUmVzaWR1YWwgUGxvdCIsIHggPSAiUHJlZGljdGVkIFZhbHVlcyIsIHkgPSAiUmVzaWR1YWxzIikgKwogIHRoZW1lX21pbmltYWwoKQoKCmdncGxvdChyZWcuc3VtbWFyeSwgYWVzKHggPSBQcmVkaWN0aW9uLCB5ID0gUmVzaWR1YWxzLCBjb2xvciA9IFJlZ3Jlc3Npb24pKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBmYWNldF93cmFwKH4gUmVncmVzc2lvbikgKyAgIyBTZXBhcmF0ZSBwbG90IGZvciBlYWNoIHJlZ3Jlc3Npb24gdHlwZQogIGxhYnModGl0bGUgPSAiUmVzaWR1YWwgUGxvdCBieSBSZWdyZXNzaW9uIFR5cGUiLCB4ID0gIlByZWRpY3RlZCBWYWx1ZXMiLCB5ID0gIlJlc2lkdWFscyIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoKSArICAjIENvcnJlY3RlZCB0byB1c2UgZGlzY3JldGUgY29sb3Igc2NhbGUKICB0aGVtZV9taW5pbWFsKCkKCmBgYAoKIyMjIEtlcm5lbCBEZW5zaXR5CgpUaGUgc2VyaWVzIG9mIHBsb3RzIHByb3ZpZGVkIGlsbHVzdHJhdGUgdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGNvbnN0cnVjdGlvbiBwZXJtaXRzIGluIFBoaWxhZGVscGhpYSB1c2luZyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgd2l0aCBkaWZmZXJlbnQgc2VhcmNoIHJhZGlpIGFuZCBhIGNvbXBhcmlzb24gb2YgdGhlc2UgZXN0aW1hdGVzIHdpdGggc3BhdGlhbCByaXNrIHByZWRpY3Rpb25zLiBUaGUgZmlyc3Qgc2V0IG9mIGltYWdlcyBzaG93cyBrZXJuZWwgZGVuc2l0eSBtYXBzIGZvciBjb25zdHJ1Y3Rpb24gcGVybWl0cyB1c2luZyB0aHJlZSBkaWZmZXJlbnQgc2VhcmNoIHJhZGlpICgxMDAwIGZ0LiwgMTUwMCBmdC4sIGFuZCAyMDAwIGZ0LikuIEFzIHRoZSBzZWFyY2ggcmFkaXVzIGluY3JlYXNlcywgdGhlIGRlbnNpdHkgbWFwIGJlY29tZXMgc21vb3RoZXIgYW5kIGJyb2FkZXIsIGluZGljYXRpbmcgYSBnZW5lcmFsaXphdGlvbiBpbiB0aGUgY29uY2VudHJhdGlvbiBvZiBjb25zdHJ1Y3Rpb24gYWN0aXZpdHkgYWNyb3NzIHRoZSBjaXR5LiBUaGlzIGhlbHBzIGlkZW50aWZ5IHdoaWNoIGFyZWFzIGFyZSBleHBlcmllbmNpbmcgaGlnaCB2b2x1bWVzIG9mIGNvbnN0cnVjdGlvbiByZWxhdGl2ZSB0byBvdGhlcnMsIHBvdGVudGlhbGx5IHBvaW50aW5nIHRvIGhvdHNwb3RzIG9mIGRldmVsb3BtZW50IG9yIGdlbnRyaWZpY2F0aW9uLgoKYGBge3Iga2QsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBkZW1vIG9mIGtlcm5lbCB3aWR0aApwZXJtaXRzX3BwcCA8LSBhcy5wcHAoc3RfY29vcmRpbmF0ZXMoY25zdFBlcm1pdHMpLCBXID0gc3RfYmJveChmaW5hbF9uZXQpKQpwZXJtaXRzX0tELjEwMDAgPC0gZGVuc2l0eS5wcHAocGVybWl0c19wcHAsIDEwMDApCnBlcm1pdHNfS0QuMTUwMCA8LSBkZW5zaXR5LnBwcChwZXJtaXRzX3BwcCwgMTUwMCkKcGVybWl0c19LRC4yMDAwIDwtZGVuc2l0eS5wcHAocGVybWl0c19wcHAsIDIwMDApCnBlcm1pdHNfS0QuZGYgPC0gcmJpbmQoCiAgbXV0YXRlKGRhdGEuZnJhbWUocmFzdGVyVG9Qb2ludHMobWFzayhyYXN0ZXIocGVybWl0c19LRC4xMDAwKSwgYXMocGhpbGx5TmVpZ2hib3Job29kcywgJ1NwYXRpYWwnKSkpKSwgTGVnZW5kID0gIjEwMDAgRnQuIiksCiAgbXV0YXRlKGRhdGEuZnJhbWUocmFzdGVyVG9Qb2ludHMobWFzayhyYXN0ZXIocGVybWl0c19LRC4xNTAwKSwgYXMocGhpbGx5TmVpZ2hib3Job29kcywgJ1NwYXRpYWwnKSkpKSwgTGVnZW5kID0gIjE1MDAgRnQuIiksCiAgbXV0YXRlKGRhdGEuZnJhbWUocmFzdGVyVG9Qb2ludHMobWFzayhyYXN0ZXIocGVybWl0c19LRC4yMDAwKSwgYXMocGhpbGx5TmVpZ2hib3Job29kcywgJ1NwYXRpYWwnKSkpKSwgTGVnZW5kID0gIjIwMDAgRnQuIikpIAoKcGVybWl0c19LRC5kZiRMZWdlbmQgPC0gZmFjdG9yKHBlcm1pdHNfS0QuZGYkTGVnZW5kLCBsZXZlbHMgPSBjKCIxMDAwIEZ0LiIsICIxNTAwIEZ0LiIsICIyMDAwIEZ0LiIpKQoKZ2dwbG90KGRhdGE9cGVybWl0c19LRC5kZiwgYWVzKHg9eCwgeT15KSkgKwogIGdlb21fcmFzdGVyKGFlcyhmaWxsPWxheWVyKSkgKyAKICBmYWNldF93cmFwKH5MZWdlbmQpICsKICBjb29yZF9zZihjcnM9c3RfY3JzKGZpbmFsX25ldCkpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWU9IkRlbnNpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJLZXJuZWwgZGVuc2l0eSB3aXRoIDMgZGlmZmVyZW50IHNlYXJjaCByYWRpaSIpICsKICB0aGVtZV92b2lkKCkKYGBgCiAKYGBge3Iga2QyLCByZXN1bHRzPSdoaWRlJywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Cgojd29ya3MgYnV0IGkgZG9udCB0aGluayBpcyBuZWVkZWQgdW5sZXNzIHRoZXJlJ3Mgb25lIHNwZWNpZmljIHRpaG5nIHdlIHdhbm5hIGxvb2sgYXQgCgphcy5kYXRhLmZyYW1lKHBlcm1pdHNfS0QuMTAwMCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhmaW5hbF9uZXQpKSAlPiUKICBhZ2dyZWdhdGUoLiwgZmluYWxfbmV0LCBtZWFuKSAlPiUKICAgZ2dwbG90KCkgKwogICAgIGdlb21fc2YoYWVzKGZpbGw9dmFsdWUpKSArCiAgICAgZ2VvbV9zZihkYXRhID0gc2FtcGxlX24oY25zdFBlcm1pdHMsIDE1MDApLCBzaXplID0gLjUpICsKICAgICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZSA9ICJEZW5zaXR5IikgKwogICAgIGxhYnModGl0bGUgPSAiS2VybmVsIGRlbnNpdHkgb2YgQ29uc3RydWN0aW9uIFBlcm1pdHMiKSArCiAgICAgdGhlbWVfdm9pZCgpCmBgYAojIyMgQ29tcGFyaW5nIG1vZGVscwoKVGhlIHBsb3RzIGJlbG93IGRlbHZlIGRlZXBlciBieSBvdmVybGF5aW5nIGFjdHVhbCBwZXJtaXQgbG9jYXRpb25zIG9uIHRoZSBkZW5zaXR5IGVzdGltYXRlcyBhbmQgY29tcGFyaW5nIHRoZXNlIGFnYWluc3Qgc3BhdGlhbCByaXNrIHByZWRpY3Rpb25zLiBUaGVzZSByaXNrIHByZWRpY3Rpb25zIGNhdGVnb3JpemUgYXJlYXMgYmFzZWQgb24gdGhlIHByZWRpY3RlZCBsZXZlbCBvZiBjb25zdHJ1Y3Rpb24gYWN0aXZpdHkgKGZyb20gIk1pbmltYWwiIHRvICJWZXJ5IEhpZ2giIHJpc2spLCBhbGxvd2luZyBmb3IgYSBudWFuY2VkIHVuZGVyc3RhbmRpbmcgb2YgZGV2ZWxvcG1lbnQgaW50ZW5zaXR5LiBUaGVzZSB2aXN1YWxpemF0aW9ucyBhcmUgY3J1Y2lhbCBhcyB0aGV5IGhpZ2hsaWdodCBhcmVhcyB3aGVyZSBpbmZyYXN0cnVjdHVyZSBhbmQgcmVndWxhdGlvbnMgbWlnaHQgbmVlZCB0byBiZSByZWFzc2Vzc2VkIGR1ZSB0byByYXBpZCBkZXZlbG9wbWVudC4gU3BlY2lmaWNhbGx5LCBhcmVhcyBjbGFzc2lmaWVkIGFzICJWZXJ5IEhpZ2ggUmlzayIgbWF5IHJlcXVpcmUgbW9yZSBhdHRlbnRpb24gdG8gbWFuYWdlIHRoZSBpbXBhY3RzIG9mIGdlbnRyaWZpY2F0aW9uLCBzdWNoIGFzIGRpc3BsYWNlbWVudCBhbmQgY2hhbmdlcyBpbiB0aGUgc29jaW8tZWNvbm9taWMgZmFicmljLiAKCmBgYCB7ciBrZF9zcGF0aWFsLCByZXN1bHRzPSdoaWRlJywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CgojIFByZWRpY3Rpb24gYnkgS2VybmVsIERlbnNpdHkgTW9kZWwKCnBlcm1pdHNfS0Rfc2ZfMjAyMiA8LSBhcy5kYXRhLmZyYW1lKHBlcm1pdHNfS0QuMTUwMCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhmaW5hbF9uZXQpKSAlPiUKICBhZ2dyZWdhdGUoLiwgZmluYWxfbmV0LCBtZWFuKSAlPiUKICBtdXRhdGUobGFiZWwgPSAiS2VybmVsIERlbnNpdHkgRXN0aW1hdGVzIiwKICAgICAgICAgUmlza19MZXZlbCA9IG50aWxlKHZhbHVlLCA1KSwgICMgQ2hhbmdlIGdyYW51bGFyaXR5IG9mIHJpc2sgbGV2ZWxzCiAgICAgICAgIFJpc2tfTGV2ZWwgPSBjYXNlX3doZW4oCiAgICAgICAgICAgUmlza19MZXZlbCA9PSA1IH4gIlZlcnkgSGlnaCBSaXNrIiwKICAgICAgICAgICBSaXNrX0xldmVsID09IDQgfiAiSGlnaCBSaXNrIiwKICAgICAgICAgICBSaXNrX0xldmVsID09IDMgfiAiTW9kZXJhdGUgUmlzayIsCiAgICAgICAgICAgUmlza19MZXZlbCA9PSAyIH4gIkxvdyBSaXNrIiwKICAgICAgICAgICBUUlVFIH4gIk1pbmltYWwgUmlzayIpKSAlPiUKICBjYmluZCgKICAgIGFnZ3JlZ2F0ZSgKICAgICAgZHBseXI6OnNlbGVjdChjbnN0UGVybWl0c18yMDIyKSAlPiUgbXV0YXRlKHBlcm1pdENvdW50ID0gMSksIC4sIHN1bSkgJT4lCiAgICAgIG11dGF0ZShwZXJtaXRDb3VudCA9IHJlcGxhY2VfbmEocGVybWl0Q291bnQsIDApKSkgJT4lCiAgZHBseXI6OnNlbGVjdChsYWJlbCwgUmlza19MZXZlbCwgcGVybWl0Q291bnQpCgojIFByZWRpY3Rpb24gYnkgUmlzayBQcmVkaWN0aW9uIE1vZGVsCgpwZXJtaXRzX0tEX3Jpc2tfc2ZfMjAyMiA8LQogIHJlZy5zcy5zcGF0aWFsQ1YgJT4lCiAgbXV0YXRlKGxhYmVsID0gIlNwYXRpYWwgUmlzayBQcmVkaWN0aW9ucyIsCiAgICAgICAgIFJpc2tfTGV2ZWwgPSBudGlsZShQcmVkaWN0aW9uLCA1KSwKICAgICAgICAgUmlza19MZXZlbCA9IGNhc2Vfd2hlbigKICAgICAgICAgICBSaXNrX0xldmVsID09IDUgfiAiVmVyeSBIaWdoIFJpc2siLAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gNCB+ICJIaWdoIFJpc2siLAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gMyB+ICJNb2RlcmF0ZSBSaXNrIiwKICAgICAgICAgICBSaXNrX0xldmVsID09IDIgfiAiTG93IFJpc2siLAogICAgICAgICAgIFRSVUUgfiAiTWluaW1hbCBSaXNrIikpICU+JQogICBjYmluZCgKICAgIGFnZ3JlZ2F0ZSgKICAgICAgZHBseXI6OnNlbGVjdChjbnN0UGVybWl0c18yMDIyKSAlPiUgbXV0YXRlKHBlcm1pdENvdW50ID0gMSksIC4sIHN1bSkgJT4lCiAgICAgIG11dGF0ZShwZXJtaXRDb3VudCA9IHJlcGxhY2VfbmEocGVybWl0Q291bnQsIDApKSkgJT4lCiAgZHBseXI6OnNlbGVjdChsYWJlbCwgUmlza19MZXZlbCwgcGVybWl0Q291bnQpCgp1bmlxdWUocGVybWl0c19LRF9zZl8yMDIyJFJpc2tfTGV2ZWwpICAjIENoZWNrIGZvciB0aGUgJ3Blcm1pdHNfS0Rfc2YnIGRhdGFzZXQKdW5pcXVlKHBlcm1pdHNfS0Rfcmlza19zZl8yMDIyJFJpc2tfTGV2ZWwpICAjIENoZWNrIGZvciB0aGUgJ3Blcm1pdHNfS0Rfcmlza19zZicgZGF0YXNldAoKc3VtbWFyeShwZXJtaXRzX0tEX3NmXzIwMjIkUmlza19MZXZlbCkKc3VtbWFyeShwZXJtaXRzX0tEX3Jpc2tfc2ZfMjAyMiRSaXNrX0xldmVsKQoKIyBFbnN1cmUgJ29wdGlvbicgYW5kICdkaXJlY3Rpb24nIGFyZSBzZXQgdG8gaGFuZGxlIGZld2VyIGNhdGVnb3JpZXMKc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIsIGRpcmVjdGlvbiA9IDEsIGJlZ2luID0gMCwgZW5kID0gMSwgbmFtZSA9ICJSaXNrIExldmVsIikKCiMgUGxvdHRpbmcgY29tcGFyaXNvbiBiZXR3ZWVuIGJvdGggbW9kZWxzCgpyYmluZChwZXJtaXRzX0tEX3NmXzIwMjIsIHBlcm1pdHNfS0Rfcmlza19zZl8yMDIyKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxhYmVsLCAtUmlza19MZXZlbCwgLWdlb21ldHJ5KSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IFJpc2tfTGV2ZWwpLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fc2YoZGF0YSA9IHNhbXBsZV9uKGNuc3RQZXJtaXRzXzIwMjIsIDExMzApLCBzaXplID0gLjUsIGNvbG91ciA9ICJibGFjayIpICsKICBmYWNldF93cmFwKH5sYWJlbCwgKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIsCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdSaXNrIExldmVsJykgKyAKICBsYWJzKHRpdGxlPSJDb21wYXJpc29uIG9mIEtlcm5lbCBEZW5zaXR5IGFuZCBSaXNrIFByZWRpY3Rpb25zIiwKICAgICAgIHN1YnRpdGxlPSJCb3R0b20gTGF5ZXIgaXMgMjAyMiBQcmVkaWN0ZWQgUGVybWl0IENvdW50cy5cbkRvdHMgYXJlIG9ic2VydmVkIDIwMjIgUGVybWl0IENvdW50cy5cbiIsCiAgICAgICBjYXB0aW9uID0gJ0ZpZ3VyZSAxOScpICsKICBtYXBUaGVtZSgpICsKICBwbG90VGhlbWUoKQoKYGBgCgpUaGUgY2hhcnQgYmVsb3cgcmV2ZWFscyBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBpbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHByZWRpY3RlZCBwZXJtaXQgY291bnRzIGJldHdlZW4gdGhlIHR3byBtb2RlbHMuIFNwYXRpYWwgUmlzayBQcmVkaWN0aW9ucyBjb25zaXN0ZW50bHkgZXN0aW1hdGUgaGlnaGVyIHJhdGVzIG9mIHBlcm1pdHMgYWNyb3NzIGFsbCByaXNrIGNhdGVnb3JpZXMgd2hlbiBjb21wYXJlZCB0byBLZXJuZWwgRGVuc2l0eSBFc3RpbWF0ZXMsIHdpdGggcGFydGljdWxhcmx5IHN0YXJrIGNvbnRyYXN0cyBpbiB0aGUgIlZlcnkgSGlnaCBSaXNrIiBhbmQgIkxvdyBSaXNrIiBjYXRlZ29yaWVzLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIFNwYXRpYWwgUmlzayBQcmVkaWN0aW9uIG1vZGVsIG1heSBiZSBtb3JlIHNlbnNpdGl2ZSB0byBmYWN0b3JzIHRoYXQgcHJlZGljdCBoaWdoZXIgY29uc3RydWN0aW9uIGFjdGl2aXR5LCBwb3RlbnRpYWxseSBwcm92aWRpbmcgYSBtb3JlIGR5bmFtaWMgb3IgbnVhbmNlZCBpbnNpZ2h0IGludG8gdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGNvbnN0cnVjdGlvbiByaXNrIGNvbXBhcmVkIHRvIHRoZSBtb3JlIGdlbmVyYWxpemVkIEtlcm5lbCBEZW5zaXR5IGFwcHJvYWNoLgoKYGBgIHtyIGNvbXBhcmlzb24gYmFyIHBsb3RzLCByZXN1bHRzPSdoaWRlJywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CgojIENvbXBhcmlzb24gb2YgcHJlZGljdGlvbnMgLSBCYXIgUGxvdHMKCnJiaW5kKHBlcm1pdHNfS0Rfc2ZfMjAyMiwgcGVybWl0c19LRF9yaXNrX3NmXzIwMjIpICU+JQogIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUgCiAgbmEub21pdCgpICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1sYWJlbCwgLVJpc2tfTGV2ZWwpICU+JQogIGdyb3VwX2J5KGxhYmVsLCBSaXNrX0xldmVsKSAlPiUKICBzdW1tYXJpemUocGVybWl0Q291bnQgPSBzdW0oVmFsdWUpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkobGFiZWwpICU+JQogIG11dGF0ZShSYXRlX29mX3Blcm1pdF9jb3VudCA9IHBlcm1pdENvdW50IC8gc3VtKHBlcm1pdENvdW50KSkgJT4lCiAgZ2dwbG90KGFlcyhSaXNrX0xldmVsLFJhdGVfb2ZfcGVybWl0X2NvdW50KSkgKwogIGdlb21fYmFyKGFlcyhmaWxsPWxhYmVsKSwgcG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIsCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdNb2RlbCcpICsKICBsYWJzKHRpdGxlID0gIlJpc2sgUHJlZGljdGlvbiB2cy4gS2VybmVsIERlbnNpdHkiLAogICAgICAgc3VidGl0bGUgPSAnUGhpbGFkZWxwaGlhLCBQQScsCiAgICAgICBjYXB0aW9uID0gJ0ZpZ3VyZSAyMCcsCiAgICAgICB4ID0gJ1Jpc2sgTGV2ZWwnLAogICAgICAgeSA9ICdSYXRlIG9mIFBlcm1pdCBDb3VudHMnKSArCiAgcGxvdFRoZW1lKCkKCgpgYGAKCiMjIyBSaXNrIE1hcHBpbmcKCk5laWdoYm9yaG9vZHMgdGhhdCBtYXkgYmUgYXQgYSBoaWdoIHJpc2sgb2YgZ2VudHJpZmljYXRpb24gYnkgdGhlIHByb3h5IG9mIG5ldyBjb25zdHJ1Y3Rpb24gcGVybWl0cyBhcmUgbW9zdGx5IGxvY2F0ZWQgbmVhciBjZW50ZXIgY2l0eS4gTW9zdCBvZiBXZXN0IFBoaWxhZGVscGhpYSwgYSBsYXJnZSBwb3J0aW9uIG9mIE5vcnRoIFBoaWxhZGVscGhpYSwgYW5kIHNvbWUgcGFydHMgb2YgdGhlIE5vcnRod2VzdCBhcmUgYXQgcmlzayBvZiBnZW50cmlmaWNhdGlvbi4gVGhlIGhpZ2hlc3QgcmlzayBuZWlnaGJvcmhvb2RzIGFyZSBQb2ludCBCcmVlemUsIEZpc2h0b3duLCBSaXR0ZW5ob3VzZSwgYW5kIE5vcnRoZXJuIExpYmVydGllcy4gCgpJbiBhZGRpdGlvbiB0byB0aGVzZSBhcmVhcywgbGFyZ2Ugc3dhdGhlcyBvZiBXZXN0IFBoaWxhZGVscGhpYSBhbmQgc2lnbmlmaWNhbnQgcG9ydGlvbnMgb2YgTm9ydGggUGhpbGFkZWxwaGlhLCBhcyB3ZWxsIGFzIHNvbWUgcGFydHMgb2YgdGhlIE5vcnRod2VzdCwgYXJlIGRlcGljdGVkIGFzIG1vZGVyYXRlIHRvIGhpZ2ggcmlzay4gVGhpcyBleHRlbnNpdmUgZGlzdHJpYnV0aW9uIHN1Z2dlc3RzIGEgYnJvYWQgaW1wYWN0IG9mIGRldmVsb3BtZW50IHRoYXQgbWF5IGluZmx1ZW5jZSBob3VzaW5nIG1hcmtldHMsIGRlbW9ncmFwaGljIHNoaWZ0cywgYW5kIGxvY2FsIGVjb25vbWllcyBhY3Jvc3MgYSBjb25zaWRlcmFibGUgcGFydCBvZiB0aGUgY2l0eS4gCgpgYGAge3IgcmlzayBtYXAsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBBc3NpZ25pbmcgUmlzayBDYXRlZ29yaWVzIGZvciBQcmVkaWN0aW9ucwoKcGVybWl0X3Jpc2tfc2ZfMjAyMiA8LQogIGZpbHRlcihyZWcuc3VtbWFyeSwgUmVncmVzc2lvbiA9PSAiU3BhdGlhbCBMT0dPLUNWOiBTcGF0aWFsIFByb2Nlc3MiKSAlPiUKICBtdXRhdGUobGFiZWwgPSAiU3BhdGlhbCBSaXNrIFByZWRpY3Rpb25zIiwKICAgICAgICAgUmlza19MZXZlbCA9IG50aWxlKFByZWRpY3Rpb24sIDUpLAogICAgICAgICBSaXNrX0xldmVsID0gY2FzZV93aGVuKAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gNSB+ICJWZXJ5IEhpZ2ggUmlzayIsCiAgICAgICAgICAgUmlza19MZXZlbCA9PSA0IH4gIkhpZ2ggUmlzayIsCiAgICAgICAgICAgUmlza19MZXZlbCA9PSAzIH4gIk1vZGVyYXRlIFJpc2siLAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gMiB+ICJMb3cgUmlzayIsCiAgICAgICAgICAgVFJVRSB+ICJNaW5pbWFsIFJpc2siKSkKCgpwZXJtaXRfcmlza19zZl8yMDIyICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1sYWJlbCwgLVJpc2tfTGV2ZWwsIC1nZW9tZXRyeSkgJT4lCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9zZihhZXMoZmlsbCA9IFJpc2tfTGV2ZWwpLCBjb2xvdXIgPSBOQSkgKwogICAgZ2VvbV9zZihkYXRhPXBoaWxseU5laWdoYm9yaG9vZHMsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFKSArCiAgICBsYWJzKHRpdGxlPSJSaXNrIFByZWRpY3Rpb25zIiwKICAgICAgICAgc3VidGl0bGU9Ik5ldyBDb25zdHJ1Y3Rpb24gUGVybWl0IFByZWRpY3Rpb25zIiwKICAgICAgICAgY2FwdGlvbj0iRmlndXJlIDIwIikgKwogICAgbWFwVGhlbWUoKSsKICAgIHBsb3RUaGVtZSgpCgoKYGBgCgpgYGAge3IgaGlnaGVzdCByaXNrIG5laWdoYm9yaG9vZCwgcmVzdWx0cz0naGlkZScsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKIyBOZWlnaGJvcmhvb2RzIGF0IGhpZ2hlc3QgcmlzayBvZiBwZXJtaXR0aW5nIGNvbnN0cnVjdGlvbgpoaWdoZXN0X3Jpc2tfbmhvb2RzIDwtCiAgcGVybWl0X3Jpc2tfc2ZfMjAyMiAlPiUKICBkcGx5cjo6c2VsZWN0KGN2SUQsIFJpc2tfTGV2ZWwsIGNvdW50UGVybWl0cykgJT4lCiAgZHBseXI6OmZpbHRlciguLCBSaXNrX0xldmVsID09ICJWZXJ5IEhpZ2ggUmlzayIpICU+JSAgIyBBZGp1c3RlZCB0byBtYXRjaCB0aGUgbmV3IHJpc2sgY2F0ZWdvcnkKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgZ3JvdXBfYnkoY3ZJRCkgJT4lCiAgc3VtbWFyaXNlKFByZWRpY3RlZF9QZXJtaXRzID0gc3VtKGNvdW50UGVybWl0cykpICU+JQogIGRwbHlyOjpmaWx0ZXIoLiwgUHJlZGljdGVkX1Blcm1pdHMgPiA1MikgJT4lCiAgYXJyYW5nZSguLCBkZXNjKFByZWRpY3RlZF9QZXJtaXRzKSkgJT4lCiAgcmVuYW1lKE5laWdoYm9yaG9vZCA9IGN2SUQpCgojIFBsb3R0aW5nIHRoZSBoaWdoZXN0IHJpc2sgbmVpZ2hib3Job29kcwpnZ3Bsb3QoaGlnaGVzdF9yaXNrX25ob29kcykgKwogIGdlb21fYmFyKGFlcyh4ID0gcmVvcmRlcihOZWlnaGJvcmhvb2QsIC1QcmVkaWN0ZWRfUGVybWl0cyksIHkgPSBQcmVkaWN0ZWRfUGVybWl0cyksIGZpbGwgPSAiI0ZFOTkwMCIsIHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNSkgKwogIGxhYnMoeCA9ICJOZWlnaGJvcmhvb2QiLCB5ID0gIlByZWRpY3RlZCBQZXJtaXRzIiwgCiAgICAgICB0aXRsZSA9ICJOZWlnaGJvcmhvb2RzIHdpdGggVmVyeSBIaWdoIFBlcm1pdHRpbmcgUmlzayIsIHN1YnRpdGxlID0gJ1BoaWxhZGVscGhpYSwgUEEnLCBjYXB0aW9uID0gIkZpZ3VyZSAyMSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgYW5nbGUgPSA2MCwgdmp1c3QgPSAwLjcsIGhqdXN0ID0gMC43KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImRhcmtyZWQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3LCBmYWNlID0gIml0YWxpYyIsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgNTAsIDgsIDUwLCAicHQiKSkKCgpgYGAKVGhlIGltcGxpY2F0aW9ucyBvZiBzdWNoIHdpZGVzcHJlYWQgZGV2ZWxvcG1lbnQgYXJlIGNvbXBsZXgsIHBvdGVudGlhbGx5IGxlYWRpbmcgdG8gaW5jcmVhc2VkIHByb3BlcnR5IHZhbHVlcyBhbmQgbGl2aW5nIGNvc3RzLCB3aGljaCBjb3VsZCBkaXNwbGFjZSBsb25nLXN0YW5kaW5nIHJlc2lkZW50cyBhbmQgYWx0ZXIgdGhlIGN1bHR1cmFsIGZhYnJpYyBvZiB0aGVzZSBuZWlnaGJvcmhvb2RzLiBUaGUgbWFwcyBoZWxwIGlkZW50aWZ5IHdoZXJlIHByb2FjdGl2ZSBtZWFzdXJlcyBtaWdodCBiZSBuZWNlc3NhcnkgdG8gbWl0aWdhdGUgdGhlIGFkdmVyc2UgZWZmZWN0cyBvZiBnZW50cmlmaWNhdGlvbiwgc3VjaCBhcyBwcm9tb3RpbmcgYWZmb3JkYWJsZSBob3VzaW5nIGluaXRpYXRpdmVzIGFuZCBzdXBwb3J0aW5nIGxvY2FsIGJ1c2luZXNzZXMgdG8gcHJlc2VydmUgbmVpZ2hib3Job29kIGNoYXJhY3RlciBhbmQgaW5jbHVzaXZpdHkuCgojIyBDb21wYXJpbmcgdG8gYSBkaWZmZXJlbnQgdGltZQoKTGV0J3Mgc2VlIGhvdyBvdXIgbW9kZWwgcGVyZm9ybWVkIHJlbGF0aXZlIHRvIEtEIG9uIGFub3RoZXIgeWVhcidzIGRhdGEuIENvbXBhcmluZyB0aGUgc3BhdGlhbCByaXNrIHByZWRpY3Rpb25zIGFuZCBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgZnJvbSAyMDEzIHRvIDIwMjIgaW4gUGhpbGFkZWxwaGlhLCB0aGVyZSBpcyBhIG5vdGljZWFibGUgc2hpZnQgaW4gcmlzayBkaXN0cmlidXRpb24gYW5kIHRoZSBjb25jZW50cmF0aW9uIG9mIHByZWRpY3RlZCBwZXJtaXQgY291bnRzLiBJbiAyMDEzLCB0aGUgdmVyeSBoaWdoLXJpc2sgY2F0ZWdvcmllcyB3ZXJlIHByZWRvbWluYW50bHkgZW1waGFzaXplZCBpbiB0aGUga2VybmVsIGRlbnNpdHkgbW9kZWwsIGluZGljYXRpbmcgYSBoaWdoZXIgY29uY2VudHJhdGlvbiBvZiBjb25zdHJ1Y3Rpb24gYWN0aXZpdHkgaW4gYSBmZXcgYXJlYXMuIEJ5IDIwMjIsIHRoZSBzcGF0aWFsIHJpc2sgcHJlZGljdGlvbnMgbW9kZWwgc2hvd3MgYSBtb3JlIGRpc3RyaWJ1dGVkIHJpc2sgYWNyb3NzIHRoZSBjaXR5LCB3aXRoIGEgc2lnbmlmaWNhbnQgcG9ydGlvbiBvZiB0aGUgY2l0eSBjbGFzc2lmaWVkIHVuZGVyIHZlcnkgaGlnaCByaXNrLCBzdWdnZXN0aW5nIGFuIGV4cGFuc2lvbiBpbiBhcmVhcyB0YXJnZXRlZCBmb3IgbmV3IGNvbnN0cnVjdGlvbi4gVGhpcyBzaGlmdCBjb3VsZCBpbXBseSBhIGJyb2FkZW5pbmcgb2YgZGV2ZWxvcG1lbnQgZm9jdXMsIHBvdGVudGlhbGx5IHNwcmVhZGluZyBnZW50cmlmaWNhdGlvbiBwcmVzc3VyZXMgbW9yZSB3aWRlbHkgYWNyb3NzIFBoaWxhZGVscGhpYS4gVGhpcyBjb21wYXJpc29uIGhpZ2hsaWdodHMgdGhlIGR5bmFtaWMgbmF0dXJlIG9mIHVyYmFuIGRldmVsb3BtZW50IGFuZCB0aGUgaW5jcmVhc2luZyBzcHJlYWQgb2YgaGlnaC1yaXNrIGFyZWFzIG92ZXIgdGhlIHllYXJzLCBuZWNlc3NpdGF0aW5nIHVwZGF0ZWQgYW5kIHJlc3BvbnNpdmUgcGxhbm5pbmcgYW5kIHBvbGljeSBtZWFzdXJlcy4KCmBgYHtyIDIwMTMgZGF0YSwgcmVzdWx0cz0naGlkZScsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKcGVybWl0c19LRF9zZl8yMDEzIDwtIGFzLmRhdGEuZnJhbWUocGVybWl0c19LRC4xNTAwKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKGZpbmFsX25ldCkpICU+JQogIGFnZ3JlZ2F0ZSguLCBmaW5hbF9uZXQsIG1lYW4pICU+JQogIG11dGF0ZShsYWJlbCA9ICJLZXJuZWwgRGVuc2l0eSBFc3RpbWF0ZXMiLAogICAgICAgICBSaXNrX0xldmVsID0gbnRpbGUodmFsdWUsIDUpLCAgIyBDaGFuZ2UgZ3JhbnVsYXJpdHkgb2YgcmlzayBsZXZlbHMKICAgICAgICAgUmlza19MZXZlbCA9IGNhc2Vfd2hlbigKICAgICAgICAgICBSaXNrX0xldmVsID09IDUgfiAiVmVyeSBIaWdoIFJpc2siLAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gNCB+ICJIaWdoIFJpc2siLAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gMyB+ICJNb2RlcmF0ZSBSaXNrIiwKICAgICAgICAgICBSaXNrX0xldmVsID09IDIgfiAiTG93IFJpc2siLAogICAgICAgICAgIFRSVUUgfiAiTWluaW1hbCBSaXNrIikpICU+JQogIGNiaW5kKAogICAgYWdncmVnYXRlKAogICAgICBkcGx5cjo6c2VsZWN0KGNuc3RQZXJtaXRzXzIwMTMpICU+JSBtdXRhdGUocGVybWl0Q291bnQgPSAxKSwgLiwgc3VtKSAlPiUKICAgICAgbXV0YXRlKHBlcm1pdENvdW50ID0gcmVwbGFjZV9uYShwZXJtaXRDb3VudCwgMCkpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGxhYmVsLCBSaXNrX0xldmVsLCBwZXJtaXRDb3VudCkKCiMgUHJlZGljdGlvbiBieSBSaXNrIFByZWRpY3Rpb24gTW9kZWwKCnBlcm1pdHNfS0Rfcmlza19zZl8yMDEzIDwtCiAgcmVnLnNzLnNwYXRpYWxDViAlPiUKICBtdXRhdGUobGFiZWwgPSAiU3BhdGlhbCBSaXNrIFByZWRpY3Rpb25zIiwKICAgICAgICAgUmlza19MZXZlbCA9IG50aWxlKFByZWRpY3Rpb24sIDUpLAogICAgICAgICBSaXNrX0xldmVsID0gY2FzZV93aGVuKAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gNSB+ICJWZXJ5IEhpZ2ggUmlzayIsCiAgICAgICAgICAgUmlza19MZXZlbCA9PSA0IH4gIkhpZ2ggUmlzayIsCiAgICAgICAgICAgUmlza19MZXZlbCA9PSAzIH4gIk1vZGVyYXRlIFJpc2siLAogICAgICAgICAgIFJpc2tfTGV2ZWwgPT0gMiB+ICJMb3cgUmlzayIsCiAgICAgICAgICAgVFJVRSB+ICJNaW5pbWFsIFJpc2siKSkgJT4lCiAgIGNiaW5kKAogICAgYWdncmVnYXRlKAogICAgICBkcGx5cjo6c2VsZWN0KGNuc3RQZXJtaXRzXzIwMTMpICU+JSBtdXRhdGUocGVybWl0Q291bnQgPSAxKSwgLiwgc3VtKSAlPiUKICAgICAgbXV0YXRlKHBlcm1pdENvdW50ID0gcmVwbGFjZV9uYShwZXJtaXRDb3VudCwgMCkpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGxhYmVsLCBSaXNrX0xldmVsLCBwZXJtaXRDb3VudCkKCnVuaXF1ZShwZXJtaXRzX0tEX3NmXzIwMTMkUmlza19MZXZlbCkgICMgQ2hlY2sgZm9yIHRoZSAncGVybWl0c19LRF9zZicgZGF0YXNldAp1bmlxdWUocGVybWl0c19LRF9yaXNrX3NmXzIwMTMkUmlza19MZXZlbCkgICMgQ2hlY2sgZm9yIHRoZSAncGVybWl0c19LRF9yaXNrX3NmJyBkYXRhc2V0CgpzdW1tYXJ5KHBlcm1pdHNfS0Rfc2ZfMjAxMyRSaXNrX0xldmVsKQpzdW1tYXJ5KHBlcm1pdHNfS0Rfcmlza19zZl8yMDEzJFJpc2tfTGV2ZWwpCgojIEVuc3VyZSAnb3B0aW9uJyBhbmQgJ2RpcmVjdGlvbicgYXJlIHNldCB0byBoYW5kbGUgZmV3ZXIgY2F0ZWdvcmllcwpzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAicGxhc21hIiwgZGlyZWN0aW9uID0gMSwgYmVnaW4gPSAwLCBlbmQgPSAxLCBuYW1lID0gIlJpc2sgTGV2ZWwiKQoKIyBQbG90dGluZyBjb21wYXJpc29uIGJldHdlZW4gYm90aCBtb2RlbHMKCnJiaW5kKHBlcm1pdHNfS0Rfc2ZfMjAxMywgcGVybWl0c19LRF9yaXNrX3NmXzIwMTMpICU+JQogIG5hLm9taXQoKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGFiZWwsIC1SaXNrX0xldmVsLCAtZ2VvbWV0cnkpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gUmlza19MZXZlbCksIGNvbG91ciA9IE5BKSArCiAgZ2VvbV9zZihkYXRhID0gc2FtcGxlX24oY25zdFBlcm1pdHNfMjAxMywgMTEzMCksIHNpemUgPSAuNSwgY29sb3VyID0gImJsYWNrIikgKwogIGZhY2V0X3dyYXAofmxhYmVsLCApICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAicGxhc21hIiwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ1Jpc2sgTGV2ZWwnKSArIAogIGxhYnModGl0bGU9IkNvbXBhcmlzb24gb2YgS2VybmVsIERlbnNpdHkgYW5kIFJpc2sgUHJlZGljdGlvbnMiLAogICAgICAgc3VidGl0bGU9IkJvdHRvbSBMYXllciBpcyAyMDEzIFByZWRpY3RlZCBQZXJtaXQgQ291bnRzLlxuRG90cyBhcmUgb2JzZXJ2ZWQgMjAxMyBQZXJtaXQgQ291bnRzLlxuIiwKICAgICAgIGNhcHRpb24gPSAnRmlndXJlIF9fJykgKwogIG1hcFRoZW1lKCkgKwogIHBsb3RUaGVtZSgpCgpgYGAKCgoKIApgYGB7ciBjb21wIHBsb3QsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KcmJpbmQocGVybWl0c19LRF9yaXNrX3NmXzIwMTMsIHBlcm1pdHNfS0Rfcmlza19zZl8yMDIyKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxhYmVsLCAtUmlza19MZXZlbCwgLWdlb21ldHJ5KSAlPiUKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGFlcyhmaWxsID0gUmlza19MZXZlbCksIGNvbG91ciA9IE5BKSArCiAgICBnZW9tX3NmKGRhdGEgPSBzYW1wbGVfbihjbnN0UGVybWl0cywgMzAwMCksIHNpemUgPSAuNSwgY29sb3VyID0gImJsYWNrIikgKwogICAgZmFjZXRfd3JhcCh+bGFiZWwsICkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSkgKwogICAgbGFicyh0aXRsZT0iQ29tcGFyaXNvbiBvZiBLZXJuZWwgRGVuc2l0eSBhbmQgUmlzayBQcmVkaWN0aW9ucyIsCiAgICAgICAgIHN1YnRpdGxlPSIyMDEzIENvbnN0cnVjdGlvbiBQZXJtaXRzIERlbnNpdHk7IDIwMjIgQ29uc3RydWN0aW9uIFBlcm1pdHMgRGVuc2l0eSIpICsKICAgIG1hcFRoZW1lKCkKCmBgYAoKYGBge3IgY29tcCBiYXIsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KcmJpbmQocGVybWl0c19LRF9zZl8yMDEzLCBwZXJtaXRzX0tEX3Jpc2tfc2ZfMjAxMykgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIG5hLm9taXQoKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGFiZWwsIC1SaXNrX0xldmVsKSAlPiUKICBncm91cF9ieShsYWJlbCwgUmlza19MZXZlbCkgJT4lCiAgc3VtbWFyaXplKGNvdW50UGVybWl0cyA9IHN1bShWYWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShsYWJlbCkgJT4lCiAgbXV0YXRlKFBjbnRfY25zdF9wZXJtaXRzID0gY291bnRQZXJtaXRzIC8gc3VtKGNvdW50UGVybWl0cykpICU+JQogICAgZ2dwbG90KGFlcyhSaXNrX0xldmVsLFBjbnRfY25zdF9wZXJtaXRzKSkgKwogICAgICBnZW9tX2JhcihhZXMoZmlsbD1sYWJlbCksIHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKwogICAgICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBuYW1lID0gIk1vZGVsIikgKwogICAgICBsYWJzKHRpdGxlID0gIlJpc2sgcHJlZGljdGlvbiB2cy4gS2VybmVsIGRlbnNpdHksIDIwMTMiLAogICAgICAgICAgIHkgPSAiJSBvZiBUZXN0IFNldCAocGVyIG1vZGVsKSIsCiAgICAgICAgICAgeCA9ICJSaXNrIExldmVsIikgKwogIHRoZW1lX2J3KCkgKwogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDAuNSkpCmBgYAoKIyMgQ29uY2x1c2lvbgoKVGhlIG1vZGVsIGNhbiBiZSB1c2VkIHRvIHBsYW4gZm9yIGZ1dHVyZSBwb2xpY3kgdGhhdCBjYW4gcHJvdGVjdCBsZWdhY3kgcmVzaWRlbnRzIGFuZCBzbWFsbCBidXNpbmVzc2VzIG9mIG5laWdoYm9yaG9vZHMgdGhhdCBhcmUgYXQgcmlzayBieSBpZGVudGlmeWluZyB0aGUgbmVpZ2hib3Job29kcyB3aGVyZSBnZW50cmlmaWNhdGlvbiBjYW4gb2NjdXIuIFRoaXMgbW9kZWwgd2FzIGJ1aWx0IHdpdGggcmVzcGVjdCB0byBuZXcgY29uc3RydWN0aW9uLCBwZXJtaXR0aW5nLCBhbmQgZGVtb2xpdGlvbiBkYXRhLCBhbW9uZyBvdGhlciBwcmVkaWN0b3JzLiBGcm9tIHRoaXMgYW5hbHlzaXMsIGl0IHdhcyBpZGVudGlmaWVkIHRoYXQgbWFueSBuZWlnaGJvcmhvb2RzIGluIHRoZSByZWdpb24gc3Vycm91bmRpbmcgQ2VudGVyIENpdHkgUGhpbGFkZWxwaGlhIGFyZSBhdCByaXNrIGZvciBnZW50cmlmaWNhdGlvbiwgZXNwZWNpYWxseSBQb2ludCBCcmVlemUsIFJpdHRlbmhvdXNlLCBhbmQgcml2ZXItYWRqYWNlbnQgbmVpZ2hib3Job29kcyBsaWtlIEVhc3QgS2Vuc2luZ3RvbiwgRmlzaHRvd24sIGFuZCBOb3J0aGVybiBMaWJlcnRpZXMuCgpPdXIgd29yayBjYW4gYmUgdXNlZCB0byBpbmZvcm0gcGxhbm5pbmcgZGVjaXNpb25zIHRoYXQgd2lsbCBiZSBjYXJyaWVkIG91dCBieSB0aGUgT2ZmaWNlIG9mIElubm92YXRpb24gYW5kIFRlY2hub2xvZ3kgYW5kIHRoZSBTbWFydCBDaXRpZXMgb2ZmaWNlLiBXaGlsZSBpdCBjYW4gYmUgYSB1c2VmdWwgdG9vbCwgaXQgc2hvdWxkIGJlIHVzZWQgaW4gdGFuZGVtIHdpdGggb3RoZXIgdG9vbHMgdG8gcHJlZGljdCBhbmQgcGxhbiBmb3IgZ2VudHJpZmljYXRpb24gaW4gZGlmZmVyZW50IG5laWdoYm9yaG9vZHMuIFRoZSBzdGFmZiBhdCBNYXJpZSBBbnRvaW5ldHRlIFByZWRpY3Rpb25zIGNob3NlIHZhcmlhYmxlcyB0aGF0IHdlcmUgcG9zc2libHkgcHJlZGljdG9ycyBvZiBnZW50cmlmaWNhdGlvbiwgYnV0IHdlIGRpZCBub3QgYWNjb3VudCBmb3IgZXZlcnl0aGluZyB0aGF0IGNvdWxkIGFmZmVjdCBhbmQgYmVjb21lIGEgcHJlZGljdG9yIGZvciBnZW50cmlmaWNhdGlvbi4gVGhlIGV4aXN0ZW5jZSBhbmQgZGlzYXBwZWFyYW5jZSBvZiBjaXZpYyBhc3NldHMgbGlrZSBzY2hvb2xzLCBsaWJyYXJpZXMsIGdyZWVuIHNwYWNlcywgYW5kIHB1YmxpYyBzcGFjZXMgY291bGQgYmUgbWFya2VycyBvZiBwb3NzaWJsZSBnZW50cmlmaWNhdGlvbiB0aGF0IGFyZSBub3QgdGllZCB0byB0aGUgZGF0YSB1c2VkIGluIHRoZSBtb2RlbC4gCgpBZGRpdGlvbmFsbHksIHRoZXJlIGFyZSBkaWZmZXJlbnQgc2l0dWF0aW9ucyBhbmQgbnVhbmNlcyBvZiBkaXNwbGFjZW1lbnQgdGhhdCBjYW5ub3QgYmUgY2FwdHVyZWQgaW4gdGhlIGRhdGEuIFByZWRpY3RpbmcgdGhlIG9yZGVyIGFuZCB0aW1lbGluZSBkaXNwbGFjZW1lbnQgaXMgYSBmdXJ0aGVyIGNoYWxsZW5nZSB0aGF0IE9JVCBhbmQgdGhlIFNtYXJ0IENpdGllcyBvZmZpY2UgY291bGQgdGFja2xlLiBEaXJlY3QgZGlzcGxhY2VtZW50LCBvciB0aGUgcGhlbm9tZW5vbiBvZiB3aGVuIHJlc2lkZW50cyBjYW4gbm8gbG9uZ2VyIGFmZm9yZCB0byBsaXZlIGluIHRoZWlyIGhvbWVzIGJlY2F1c2Ugb2YgcmlzaW5nIGNvc3RzLCB3b3VsZCBvY2N1ciBmaXJzdCB3aXRoIHRoZSBwcm9wb3NhbHMgYW5kIG5ldyBjb25zdHJ1Y3Rpb25zIGFuZCBvdGhlciB0eXBlcyBvZiBwZXJtaXRzXjheLiBJbmRpcmVjdCBkaXNwbGFjZW1lbnQgb2NjdXJzIHdoZW4gaGlnaGVyIGluY29tZSByZXNpZGVudHMgbW92ZSBpbnRvIGFuIGFyZWEgd2hlbiBsb3dlciBpbmNvbWUgcmVzaWRlbnRzIG1vdmUgb3V0LiBUaGlzIGNhbiBiZSBjYXB0dXJlZCB0aHJvdWdoIGNlbnN1cyBkYXRhLCBpbiB0aGUgbWVkaWFuIGluY29tZSBvciBpbmNvbWUgYnJhY2tldCBicmVha2Rvd24sIHRvIHNlZSB0aGUgZmluYW5jaWFsIGxhbmRzY2FwZSBvZiB0aGUgbmVpZ2hib3Job29kIGNoYW5nZS4gRmluYWxseSwgdGhlcmUgaXMgY3VsdHVyYWwgZGlzcGxhY2VtZW50IHRoYXQgb2NjdXJzIHdoZW4gY2l2aWMgYXNzZXRzIGNoYW5nZS4gU2hvcHMgY2hhbmdlIHRvIGZvY3VzIHRoZWlyIGN1c3RvbWVyIGJhc2Ugb24gbmV3IHBlb3BsZSwgYW5kIGl0IGJyb2FkbHkgZmVlbHMgbGlrZSB0aGUgbmVpZ2hib3Job29kIGhhcyB0cmFuc2Zvcm1lZF4xMF4uIEEgbW9kZWwgdGhhdCBjYW4gdHJhY2sgYSBuZWlnaGJvcmhvb2QgdXNpbmcgZGF0YSB0aHJvdWdob3V0IHRoZXNlIHBoYXNlcyBpcyBvbmUgdGhhdCBzaG91bGQgYmUgdXNlZCBieSB0aGUgY2l0eSB0byBpZGVudGlmeSBnZW50cmlmaWNhdGlvbi4gCgojIyBSZWZlcmVuY2VzCgoxLiBodHRwczovL3doeXkub3JnL2FydGljbGVzL3BoaWxhZGVscGhpYS1hbWVyaWNhcy1wb29yZXN0LWJpZy1jaXR5LXBvdmVydHkvCjIuIGh0dHBzOi8vd3d3LnBld3RydXN0cy5vcmcvLS9tZWRpYS9hc3NldHMvMjAyMC8wOS9waGlsbHlob3VzaW5ncmVwb3J0LnBkZgozLiBJYmlkLgo0LiBodHRwczovL3doeXkub3JnL2FydGljbGVzL3BoaWxhZGVscGhpYS1hZmZvcmRhYmxlLWhvdXNpbmctYmlsbHMtY291bmNpbC1nYXV0aGllci8KNS4gaHR0cHM6Ly9zaXRlcy51dGV4YXMuZWR1L2dlbnRyaWZpY2F0aW9ucHJvamVjdC91bmRlcnN0YW5kaW5nLWdlbnRyaWZpY2F0aW9uLWFuZC1kaXNwbGFjZW1lbnQvCjYuIGh0dHBzOi8vaGFpLnN0YW5mb3JkLmVkdS9uZXdzL3Nwb3R0aW5nLXZpc3VhbC1zaWducy1nZW50cmlmaWNhdGlvbi1zY2FsZQo3LiBodHRwczovL29wZW5kYXRhcGhpbGx5Lm9yZy8=